1use std::ops::Deref;
2
3use crate::{Endpoint, QuartzResult};
4use hyper::{Body, Request, Response};
5
6enum CurlOption {
7 Location,
8 Request,
9 Header,
10 Data,
11}
12
13#[derive(clap::Args, Debug)]
14pub struct Curl {
15 #[arg(long)]
17 long: bool,
18
19 #[arg(long)]
21 multiline: bool,
22}
23
24impl Curl {
25 pub fn print(&self, endpoint: &mut Endpoint) -> QuartzResult {
26 let separator = if self.multiline { " \\\n\t" } else { " " };
27
28 print!(
29 "curl {} '{}'",
30 self.option_string(CurlOption::Location),
31 endpoint.full_url().unwrap()
32 );
33 print!(
34 " {} {}",
35 self.option_string(CurlOption::Request),
36 endpoint.method
37 );
38
39 for (key, value) in endpoint.headers.iter() {
40 print!(
41 "{}{} '{}: {}'",
42 separator,
43 self.option_string(CurlOption::Header),
44 key,
45 value
46 );
47 }
48
49 if let Some(body) = endpoint.body() {
50 let mut body = body.to_owned();
51 print!("{}{} '", separator, self.option_string(CurlOption::Data));
52
53 if body.ends_with('\n') {
54 body.truncate(body.len() - 1);
55 }
56
57 print!("{body}");
58 println!("'");
59 } else {
60 println!();
61 }
62
63 Ok(())
64 }
65
66 fn option_string(&self, option: CurlOption) -> String {
67 let result = match option {
68 CurlOption::Location => {
69 if self.long {
70 "--location"
71 } else {
72 "-L"
73 }
74 }
75 CurlOption::Request => {
76 if self.long {
77 "--request"
78 } else {
79 "-X"
80 }
81 }
82 CurlOption::Header => {
83 if self.long {
84 "--header"
85 } else {
86 "-H"
87 }
88 }
89 CurlOption::Data => {
90 if self.long {
91 "--data"
92 } else {
93 "-d"
94 }
95 }
96 };
97
98 result.to_string()
99 }
100}
101
102pub struct Http(String);
103
104impl Deref for Http {
105 type Target = String;
106
107 fn deref(&self) -> &Self::Target {
108 &self.0
109 }
110}
111
112impl From<&Response<Body>> for Http {
113 fn from(value: &Response<Body>) -> Self {
114 let mut output = String::new();
115
116 output.push_str(&format!("< {:?}", value.version()));
117 output.push_str(&format!(" {:?}", value.status()));
118 output.push('\n');
119
120 for (k, v) in value.headers().iter() {
121 output.push_str(&format!(
122 "< {}: {}\n",
123 k.as_str(),
124 v.to_str().unwrap_or_default()
125 ))
126 }
127
128 output.push('<');
129
130 Self(output)
131 }
132}
133
134impl From<&Request<Body>> for Http {
135 fn from(value: &Request<Body>) -> Self {
136 let mut output = String::new();
137
138 output.push_str(&format!(
139 "> {} {} {:?}\n",
140 value.method(),
141 value.uri().path_and_query().unwrap().as_str(),
142 value.version()
143 ));
144 output.push_str(&format!("> Host: {}\n", value.uri().host().unwrap()));
145
146 for (k, v) in value.headers().iter() {
147 output.push_str(&format!(
148 "> {}: {}\n",
149 k.as_str(),
150 v.to_str().unwrap_or_default()
151 ))
152 }
153
154 output.push('>');
155
156 Self(output)
157 }
158}
159
160impl Http {
161 pub fn print(endpoint: &mut Endpoint) -> QuartzResult {
162 let url = endpoint.full_url()?;
163 let path = url.path_and_query().unwrap();
164
165 println!("{} {} HTTP/1.1", endpoint.method, path.as_str());
166 println!("Host: {}", url.host().unwrap());
167 print!("{}", endpoint.headers);
168
169 if let Some(body) = endpoint.body() {
170 println!();
171 print!("{body}");
172 }
173
174 Ok(())
175 }
176}