From afc554f4978703f03c8d337ae2dcf78a11c0f5bd Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 21 Oct 2024 16:30:43 +0200 Subject: [PATCH] feat: client is fillable too, generalized trait --- Cargo.lock | 2 +- src/ext.rs | 20 +++++++++ src/model/client.rs | 22 ++++++++++ src/model/endpoint.rs | 96 ++++++++++++++++++++----------------------- 4 files changed, 87 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3827fd3..b4935f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -815,7 +815,7 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "postwoman" -version = "0.4.0" +version = "0.4.1" dependencies = [ "base64", "chrono", diff --git a/src/ext.rs b/src/ext.rs index a0ad897..f0f2887 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -38,3 +38,23 @@ pub fn stringify_json(v: &serde_json::Value) -> String { pub fn full_name(namespace: &str, name: &str) -> String { format!("{namespace}:{name}") } + +pub trait FillableFromEnvironment { + fn fill(self, env: &toml::Table) -> Self; + + fn default_vars(env: &toml::Table) -> std::collections::HashMap { + let mut vars: std::collections::HashMap = std::collections::HashMap::default(); + + vars.insert("POSTWOMAN_TIMESTAMP".to_string(), chrono::Local::now().timestamp().to_string()); + + for (k, v) in env { + vars.insert(k.to_string(), stringify_toml(v)); + } + + for (k, v) in std::env::vars() { + vars.insert(k, v); + } + + vars + } +} diff --git a/src/model/client.rs b/src/model/client.rs index ad7bf04..a9ea0c6 100644 --- a/src/model/client.rs +++ b/src/model/client.rs @@ -1,3 +1,5 @@ +use crate::ext::FillableFromEnvironment; + #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] pub struct ClientConfig { @@ -12,3 +14,23 @@ pub struct ClientConfig { /// accept invalid SSL certificates, defaults to false (be careful: this is dangerous!) pub accept_invalid_certs: Option, } + +impl FillableFromEnvironment for ClientConfig { + fn fill(mut self, env: &toml::Table) -> Self { + let vars = Self::default_vars(env); + + for (k, v) in vars { + let k_var = format!("${{{k}}}"); + + if let Some(base) = self.base { + self.base = Some(base.replace(&k_var, &v)); + } + + if let Some(user_agent) = self.user_agent { + self.user_agent = Some(user_agent.replace(&k_var, &v)); + } + } + + self + } +} diff --git a/src/model/endpoint.rs b/src/model/endpoint.rs index 0e5d1e0..8851e11 100644 --- a/src/model/endpoint.rs +++ b/src/model/endpoint.rs @@ -8,7 +8,7 @@ use jaq_interpret::FilterT; use crate::errors::InvalidHeaderError; use crate::{PostWomanError, APP_USER_AGENT}; -use crate::ext::{stringify_toml, stringify_json, StringOr}; +use crate::ext::{stringify_json, stringify_toml, FillableFromEnvironment, StringOr}; use super::{ExtractorConfig, ClientConfig}; @@ -75,57 +75,6 @@ impl EndpointConfig { url } - pub fn fill(mut self, env: &toml::Table) -> Self { - let mut vars: HashMap = HashMap::default(); - - vars.insert("POSTWOMAN_TIMESTAMP".to_string(), chrono::Local::now().timestamp().to_string()); - - for (k, v) in env { - vars.insert(k.to_string(), stringify_toml(v)); - } - - for (k, v) in std::env::vars() { - vars.insert(k, v); - } - - for (k, v) in vars { - let k_var = format!("${{{k}}}"); - self.path = self.path.replace(&k_var, &v); - if let Some(method) = self.method { - self.method = Some(method.replace(&k_var, &v)); - } - if let Some(b) = self.body { - match b { - StringOr::Str(body) => { - self.body = Some(StringOr::Str(body.replace(&k_var, &v))); - }, - StringOr::T(json) => { - let wrap = toml::Value::Table(json.clone()); - let toml::Value::Table(out) = replace_recursive(wrap, &k_var, &v) - else { unreachable!("we put in a table, we get out a table") }; - self.body = Some(StringOr::T(out)); - }, - } - } - if let Some(query) = self.query { - self.query = Some( - query.into_iter() - .map(|x| x.replace(&k_var, &v)) - .collect() - ); - } - if let Some(headers) = self.headers { - self.headers = Some( - headers.into_iter() - .map(|x| x.replace(&k_var, &v)) - .collect() - ); - } - } - - self - } - pub async fn execute(self, opts: &ClientConfig) -> Result { let body = self.body()?; let method = self.method()?; @@ -196,6 +145,49 @@ impl EndpointConfig { } } +impl FillableFromEnvironment for EndpointConfig { + fn fill(mut self, env: &toml::Table) -> Self { + let vars = Self::default_vars(env); + + for (k, v) in vars { + let k_var = format!("${{{k}}}"); + self.path = self.path.replace(&k_var, &v); + if let Some(method) = self.method { + self.method = Some(method.replace(&k_var, &v)); + } + if let Some(b) = self.body { + match b { + StringOr::Str(body) => { + self.body = Some(StringOr::Str(body.replace(&k_var, &v))); + }, + StringOr::T(json) => { + let wrap = toml::Value::Table(json.clone()); + let toml::Value::Table(out) = replace_recursive(wrap, &k_var, &v) + else { unreachable!("we put in a table, we get out a table") }; + self.body = Some(StringOr::T(out)); + }, + } + } + if let Some(query) = self.query { + self.query = Some( + query.into_iter() + .map(|x| x.replace(&k_var, &v)) + .collect() + ); + } + if let Some(headers) = self.headers { + self.headers = Some( + headers.into_iter() + .map(|x| x.replace(&k_var, &v)) + .collect() + ); + } + } + + self + } +} + fn replace_recursive(element: toml::Value, from: &str, to: &str) -> toml::Value { match element { toml::Value::Float(x) => toml::Value::Float(x),