feat: initial work on detecting vars
i want to throw errors when vars are missing, so that its easier to find and fill them when running pw
This commit is contained in:
parent
ac92c53799
commit
40f2c0a6d4
3 changed files with 79 additions and 50 deletions
|
@ -42,6 +42,9 @@ pub enum PostWomanError {
|
||||||
|
|
||||||
#[error("regex failed matching in content: {0}")]
|
#[error("regex failed matching in content: {0}")]
|
||||||
NoMatch(String),
|
NoMatch(String),
|
||||||
|
|
||||||
|
#[error("missing environment variable: {0}")]
|
||||||
|
MissingVar(#[from] crate::ext::FillError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
35
src/ext.rs
35
src/ext.rs
|
@ -1,3 +1,7 @@
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use crate::PostWomanError;
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum StringOr<T> {
|
pub enum StringOr<T> {
|
||||||
|
@ -34,13 +38,40 @@ pub fn stringify_json(v: &serde_json::Value) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn var_matcher() -> &'static regex::Regex {
|
||||||
|
static MATCHER : OnceLock<regex::Regex> = OnceLock::new();
|
||||||
|
MATCHER.get_or_init(|| regex::Regex::new(r"\$\{(.*)\}").expect("wrong matcher regex"))
|
||||||
|
}
|
||||||
|
|
||||||
// keep it as separate fn so we can change it everywhere easily
|
// keep it as separate fn so we can change it everywhere easily
|
||||||
pub fn full_name(namespace: &str, name: &str) -> String {
|
pub fn full_name(namespace: &str, name: &str) -> String {
|
||||||
format!("{namespace}:{name}")
|
format!("{namespace}:{name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FillableFromEnvironment {
|
#[derive(Debug, thiserror::Error)]
|
||||||
fn fill(self, env: &toml::Table) -> Self;
|
#[error("could not fill {0}")]
|
||||||
|
pub struct FillError(pub String);
|
||||||
|
|
||||||
|
pub trait FillableFromEnvironment: Sized {
|
||||||
|
fn fill(self, env: &toml::Table) -> Result<Self, FillError>;
|
||||||
|
|
||||||
|
fn replace(mut from: String, env: &toml::Table) -> Result<String, FillError> {
|
||||||
|
let placeholders: Vec<(String, String)> = var_matcher()
|
||||||
|
.captures_iter(&from)
|
||||||
|
.map(|m| m.extract())
|
||||||
|
.map(|(txt, [var])| (txt.to_string(), var.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// TODO can we avoid cloning all matches??? can't mutate `from` as captures_iter holds an
|
||||||
|
// immutable reference to original string
|
||||||
|
|
||||||
|
for (txt, var) in placeholders {
|
||||||
|
let value = env.get(&var).ok_or(FillError(var.to_string()))?;
|
||||||
|
from = from.replace(&txt, &stringify_toml(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(from)
|
||||||
|
}
|
||||||
|
|
||||||
fn default_vars(env: &toml::Table) -> std::collections::HashMap<String, String> {
|
fn default_vars(env: &toml::Table) -> std::collections::HashMap<String, String> {
|
||||||
let mut vars: std::collections::HashMap<String, String> = std::collections::HashMap::default();
|
let mut vars: std::collections::HashMap<String, String> = std::collections::HashMap::default();
|
||||||
|
|
|
@ -8,7 +8,7 @@ use jaq_interpret::FilterT;
|
||||||
use crate::errors::InvalidHeaderError;
|
use crate::errors::InvalidHeaderError;
|
||||||
use crate::{PostWomanError, APP_USER_AGENT};
|
use crate::{PostWomanError, APP_USER_AGENT};
|
||||||
|
|
||||||
use crate::ext::{stringify_json, FillableFromEnvironment, StringOr};
|
use crate::ext::{stringify_json, var_matcher, FillError, FillableFromEnvironment, StringOr};
|
||||||
use super::{ExtractorConfig, ClientConfig};
|
use super::{ExtractorConfig, ClientConfig};
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,71 +146,66 @@ impl EndpointConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FillableFromEnvironment for EndpointConfig {
|
impl FillableFromEnvironment for EndpointConfig {
|
||||||
fn fill(mut self, env: &toml::Table) -> Self {
|
fn fill(mut self, env: &toml::Table) -> Result<Self, FillError> {
|
||||||
let vars = Self::default_vars(env);
|
let vars = Self::default_vars(env);
|
||||||
|
|
||||||
for (k, v) in vars {
|
self.path = Self::replace(self.path, env)?;
|
||||||
let k_var = format!("${{{k}}}");
|
|
||||||
self.path = self.path.replace(&k_var, &v);
|
|
||||||
if let Some(method) = self.method {
|
if let Some(method) = self.method {
|
||||||
self.method = Some(method.replace(&k_var, &v));
|
self.method = Some(Self::replace(method, env)?);
|
||||||
}
|
}
|
||||||
if let Some(b) = self.body {
|
if let Some(b) = self.body {
|
||||||
match b {
|
match b {
|
||||||
StringOr::Str(body) => {
|
StringOr::Str(body) => {
|
||||||
self.body = Some(StringOr::Str(body.replace(&k_var, &v)));
|
self.body = Some(StringOr::Str(Self::replace(body, env)?));
|
||||||
},
|
},
|
||||||
StringOr::T(json) => {
|
StringOr::T(json) => {
|
||||||
let wrap = toml::Value::Table(json.clone());
|
let wrap = toml::Value::Table(json.clone());
|
||||||
let toml::Value::Table(out) = replace_recursive(wrap, &k_var, &v)
|
let toml::Value::Table(out) = replace_recursive(wrap, env)?
|
||||||
else { unreachable!("we put in a table, we get out a table") };
|
else { unreachable!("we put in a table, we get out a table") };
|
||||||
self.body = Some(StringOr::T(out));
|
self.body = Some(StringOr::T(out));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(query) = self.query {
|
if let Some(query) = self.query {
|
||||||
self.query = Some(
|
for q in query {
|
||||||
query.into_iter()
|
q = Self::replace(q, env)?;
|
||||||
.map(|x| x.replace(&k_var, &v))
|
}
|
||||||
.collect()
|
self.query = Some(query);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if let Some(headers) = self.headers {
|
if let Some(headers) = self.headers {
|
||||||
self.headers = Some(
|
for h in headers {
|
||||||
headers.into_iter()
|
h = Self::replace(h, env)?;
|
||||||
.map(|x| x.replace(&k_var, &v))
|
}
|
||||||
.collect()
|
self.headers = Some(headers);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
fn replace_recursive(element: toml::Value, env: &toml::Table) -> Result<toml::Value, FillError> {
|
||||||
}
|
Ok(match element {
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_recursive(element: toml::Value, from: &str, to: &str) -> toml::Value {
|
|
||||||
match element {
|
|
||||||
toml::Value::Float(x) => toml::Value::Float(x),
|
toml::Value::Float(x) => toml::Value::Float(x),
|
||||||
toml::Value::Integer(x) => toml::Value::Integer(x),
|
toml::Value::Integer(x) => toml::Value::Integer(x),
|
||||||
toml::Value::Boolean(x) => toml::Value::Boolean(x),
|
toml::Value::Boolean(x) => toml::Value::Boolean(x),
|
||||||
toml::Value::Datetime(x) => toml::Value::Datetime(x),
|
toml::Value::Datetime(x) => toml::Value::Datetime(x),
|
||||||
toml::Value::String(x) => toml::Value::String(x.replace(from, to)),
|
toml::Value::String(x) => toml::Value::String(EndpointConfig::replace(x, env)?),
|
||||||
toml::Value::Array(x) => toml::Value::Array(
|
toml::Value::Array(arr) => {
|
||||||
x.into_iter().map(|x| replace_recursive(x, from, to)).collect()
|
for v in arr.iter_mut() {
|
||||||
),
|
*v = replace_recursive(v, env)?;
|
||||||
|
}
|
||||||
|
toml::Value::Array(arr)
|
||||||
|
},
|
||||||
toml::Value::Table(map) => {
|
toml::Value::Table(map) => {
|
||||||
let mut out = toml::map::Map::new();
|
let mut out = toml::map::Map::new();
|
||||||
for (k, v) in map {
|
for (k, v) in map {
|
||||||
let new_v = replace_recursive(v.clone(), from, to);
|
let new_v = replace_recursive(v.clone(), env)?;
|
||||||
if k.contains(from) {
|
let new_k = EndpointConfig::replace(k, env)?;
|
||||||
out.insert(k.replace(from, to), new_v);
|
out.insert(new_k, new_v);
|
||||||
} else {
|
|
||||||
out.insert(k.to_string(), new_v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
toml::Value::Table(out)
|
toml::Value::Table(out)
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn format_body(res: reqwest::Response) -> Result<String, PostWomanError> {
|
async fn format_body(res: reqwest::Response) -> Result<String, PostWomanError> {
|
||||||
|
|
Loading…
Reference in a new issue