ploidy_codegen_rust/
client.rs1use ploidy_core::codegen::IntoCode;
2use proc_macro2::TokenStream;
3use quote::{ToTokens, TokenStreamExt, quote};
4
5use super::{
6 cfg::CfgFeature,
7 graph::CodegenGraph,
8 naming::{CargoFeature, CodegenIdentUsage},
9};
10
11#[derive(Clone, Copy, Debug)]
13pub struct CodegenClientModule<'a> {
14 graph: &'a CodegenGraph<'a>,
15 features: &'a [&'a CargoFeature],
16}
17
18impl<'a> CodegenClientModule<'a> {
19 pub fn new(graph: &'a CodegenGraph<'a>, features: &'a [&'a CargoFeature]) -> Self {
20 Self { graph, features }
21 }
22}
23
24impl ToTokens for CodegenClientModule<'_> {
25 fn to_tokens(&self, tokens: &mut TokenStream) {
26 let mods = self.features.iter().map(|feature| {
27 let cfg = CfgFeature::for_resource_module(feature);
28 let mod_name = CodegenIdentUsage::Module(feature.as_ident());
29 quote! {
30 #cfg
31 pub mod #mod_name;
32 }
33 });
34
35 let client_doc = self.graph.info().label().map(|label| {
36 let doc = match label.version {
37 Some(version) => format!("API client for {} (version {version})", label.title),
38 None => format!("API client for {}", label.title),
39 };
40 quote! { #[doc = #doc] }
41 });
42
43 tokens.append_all(quote! {
44 #client_doc
45 #[derive(Clone, Debug)]
46 pub struct Client {
47 client: ::ploidy_util::reqwest::Client,
48 headers: ::ploidy_util::http::HeaderMap,
49 base_url: ::ploidy_util::url::Url,
50 }
51
52 impl Client {
53 pub fn new(base_url: impl AsRef<str>) -> Result<Self, crate::error::Error> {
55 Ok(Self::with_reqwest_client(
56 ::ploidy_util::reqwest::Client::new(),
57 base_url.as_ref().parse()?,
58 ))
59 }
60
61 pub fn with_reqwest_client(
62 client: crate::util::reqwest::Client,
63 base_url: crate::util::url::Url,
64 ) -> Self {
65 Self {
66 client,
67 headers: ::ploidy_util::http::HeaderMap::new(),
68 base_url,
69 }
70 }
71
72 pub fn with_header<K, V>(mut self, name: K, value: V) -> Result<Self, crate::error::Error>
74 where
75 K: TryInto<crate::util::http::HeaderName>,
76 V: TryInto<crate::util::http::HeaderValue>,
77 K::Error: Into<crate::util::http::Error>,
78 V::Error: Into<crate::util::http::Error>,
79 {
80 let name = name
81 .try_into()
82 .map_err(|err| crate::error::Error::BadHeaderName(err.into()))?;
83 let value = value
84 .try_into()
85 .map_err(|err| crate::error::Error::BadHeaderValue(name.clone(), err.into()))?;
86 self.headers.insert(name, value);
87 Ok(Self {
88 client: self.client,
89 headers: self.headers,
90 base_url: self.base_url,
91 })
92 }
93
94 pub fn with_sensitive_header<K, V>(self, name: K, value: V) -> Result<Self, crate::error::Error>
107 where
108 K: TryInto<crate::util::http::HeaderName>,
109 V: TryInto<crate::util::http::HeaderValue>,
110 K::Error: Into<crate::util::http::Error>,
111 V::Error: Into<crate::util::http::Error>,
112 {
113 let name = name
114 .try_into()
115 .map_err(|err| crate::error::Error::BadHeaderName(err.into()))?;
116 let mut value: ::ploidy_util::http::HeaderValue = value
117 .try_into()
118 .map_err(|err| crate::error::Error::BadHeaderValue(name.clone(), err.into()))?;
119 value.set_sensitive(true);
120 self.with_header(name, value)
121 }
122
123 pub fn with_user_agent<V>(self, value: V) -> Result<Self, crate::error::Error>
124 where
125 V: TryInto<crate::util::http::HeaderValue>,
126 V::Error: Into<crate::util::http::Error>,
127 {
128 self.with_header(::ploidy_util::http::header::USER_AGENT, value)
129 }
130
131 pub fn request(
146 &self,
147 method: crate::util::reqwest::Method,
148 path_and_query: &str,
149 ) -> Result<crate::util::reqwest::RequestBuilder, crate::error::Error> {
150 let parts: ::ploidy_util::http::uri::PathAndQuery = path_and_query.parse()?;
151 let mut url = self.base_url.clone();
152 let _ = url.path_segments_mut().map(|mut segments| {
153 let path = parts.path();
154 if path != "/" {
155 let path = path
156 .strip_prefix('/') .unwrap_or(path);
158 segments
159 .pop_if_empty() .extend(path.split('/'));
161 }
162 });
163 if let Some(query) = parts.query() {
164 url.query_pairs_mut()
165 .extend_pairs(::ploidy_util::url::form_urlencoded::parse(query.as_bytes()));
166 }
167 Ok(self.client
168 .request(method, url)
169 .headers(self.headers.clone()))
170 }
171 }
172
173 #(#mods)*
174 });
175 }
176}
177
178impl IntoCode for CodegenClientModule<'_> {
179 type Code = (&'static str, TokenStream);
180
181 fn into_code(self) -> Self::Code {
182 ("src/client/mod.rs", self.into_token_stream())
183 }
184}