pact_broker_cli/cli/
utils.rs1use std::str::FromStr;
2
3use console::Style;
4use tracing_core::LevelFilter;
5use tracing_subscriber::layer::SubscriberExt;
6use tracing_subscriber::util::SubscriberInitExt;
7
8pub fn setup_loggers(level: &str) {
9 let log_level = match level {
10 "none" => LevelFilter::OFF,
11 _ => LevelFilter::from_str(level).unwrap_or(LevelFilter::INFO),
12 };
13
14 tracing_subscriber::registry()
15 .with({
16 if log_level != LevelFilter::OFF {
17 Some(
18 tracing_subscriber::fmt::layer()
19 .compact()
20 .with_thread_names(true)
21 .with_level(true),
22 )
23 } else {
24 None
25 }
26 })
27 .with({
28 if log_level != LevelFilter::OFF {
29 Some(tracing_subscriber::filter::LevelFilter::from_level(
30 log_level.into_level().unwrap(),
31 ))
32 } else {
33 None
34 }
35 })
36 .try_init()
37 .unwrap_or_else(|err| eprintln!("ERROR: Failed to initialise loggers - {err}"));
38}
39
40pub fn glob_value(v: String) -> Result<String, String> {
41 match glob::Pattern::new(&v) {
42 Ok(res) => Ok(res.to_string()),
43 Err(err) => Err(format!("'{}' is not a valid glob pattern - {}", v, err)),
44 }
45}
46
47pub const RED: Style = Style::new().red();
48pub const GREEN: Style = Style::new().green();
49pub const YELLOW: Style = Style::new().yellow();
50pub const CYAN: Style = Style::new().cyan();
51
52#[macro_export]
55macro_rules! dbg_as_curl {
56 ($req:expr) => {
57 match $req {
58 tmp => {
59 match tmp.try_clone().map(|b| b.build()) {
60 Some(Ok(req)) => tracing::debug!("{}", crate::cli::utils::AsCurl::new(&req)),
61 Some(Err(err)) => tracing::debug!("*Error*: {}", err),
62 None => tracing::debug!("*Error*: request not cloneable",),
63 }
64 tmp
65 }
66 }
67 };
68}
69
70pub struct AsCurl<'a> {
72 req: &'a reqwest::Request,
73}
74
75impl<'a> AsCurl<'a> {
76 pub fn new(req: &'a reqwest::Request) -> AsCurl<'a> {
78 Self { req }
79 }
80}
81
82impl<'a> std::fmt::Debug for AsCurl<'a> {
83 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
84 <Self as std::fmt::Display>::fmt(self, f)
85 }
86}
87
88impl<'a> std::fmt::Display for AsCurl<'a> {
89 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90 let AsCurl { req } = *self;
91
92 write!(f, "curl ")?;
93
94 let method = req.method();
95 if method != "GET" {
96 write!(f, "-X {} ", method)?;
97 }
98
99 for (name, value) in req.headers() {
100 let value = value
101 .to_str()
102 .expect("Headers must contain only visible ASCII characters")
103 .replace("'", r"'\''");
104
105 write!(f, "--header '{}: {}' ", name, value)?;
106 }
107
108 if let Some(body) = req.body() {
110 if let Some(bytes) = body.as_bytes() {
112 let s = String::from_utf8_lossy(bytes).replace("'", r"'\''");
113 write!(f, "--data-raw '{}' ", s)?;
114 } else {
115 write!(
116 f,
117 "# NOTE: Body present but not shown (stream or unknown type) "
118 )?;
119 }
120 }
121
122 write!(f, "'{}'", req.url().to_string().replace("'", "%27"))?;
124
125 Ok(())
126 }
127}
128
129#[cfg(test)]
130mod debug_as_curl_tests {
131
132 use crate::dbg_as_curl;
133
134 fn compare(req: reqwest::RequestBuilder, result: &str) {
135 let req = dbg_as_curl!(req);
136
137 let req = req.build().unwrap();
138 assert_eq!(format!("{}", super::AsCurl::new(&req)), result);
139 }
140
141 #[test]
142 fn basic() {
143 let client = reqwest::Client::new();
144
145 compare(
146 client.get("http://example.org"),
147 "curl 'http://example.org/'",
148 );
149 compare(
150 client.get("https://example.org"),
151 "curl 'https://example.org/'",
152 );
153 }
154
155 #[test]
156 fn escape_url() {
157 let client = reqwest::Client::new();
158
159 compare(
160 client.get("https://example.org/search?q='"),
161 "curl 'https://example.org/search?q=%27'",
162 );
163 }
164
165 #[test]
166 fn bearer() {
167 let client = reqwest::Client::new();
168
169 compare(
170 client.get("https://example.org").bearer_auth("foo"),
171 "curl --header 'authorization: Bearer foo' 'https://example.org/'",
172 );
173 }
174
175 #[test]
176 fn escape_headers() {
177 let client = reqwest::Client::new();
178
179 compare(
180 client.get("https://example.org").bearer_auth("test's"),
181 r"curl --header 'authorization: Bearer test'\''s' 'https://example.org/'",
182 );
183 }
184
185 #[test]
187 fn body() {
188 let client = reqwest::Client::new();
189
190 compare(
191 client.get("https://example.org").body("test's"),
192 r"curl --data-raw 'test'\''s' 'https://example.org/'",
193 );
194 }
195}