1use proc_macro2::Ident;
17use proc_macro2::TokenStream;
18use quote::format_ident;
19use quote::quote;
20
21use crate::emit::HEADER;
22use crate::emit::Targets;
23use crate::emit::axum;
24use crate::emit::operation;
25use crate::emit::render_body;
26use crate::emit::reqwest;
27use crate::emit::usage;
28use crate::error::Result;
29use crate::ir::Module;
30use crate::ir::ServerUrls;
31use crate::ir::Service;
32use crate::naming::RustIdent;
33use crate::naming::operations::axum_handler_name;
34use crate::package::GeneratedFile;
35use crate::package::GeneratedPackage;
36
37const MODELS: &str = "models";
39const SERVER_URLS: &str = "server_urls";
41const OPERATIONS: &str = "operations";
43const SERVER: &str = "server";
45const CLIENT: &str = "client";
47
48const OPERATION_DEPTH: usize = 2;
50const MODULE_DEPTH: usize = 1;
52
53pub fn emit_package(
58 module: &Module,
59 service: &Service,
60 server_urls: Option<&ServerUrls>,
61 targets: Targets,
62 stem: &str,
63) -> Result<GeneratedPackage> {
64 let derives = usage::model_derives(module, service, targets);
65 let foreign = usage::foreign_resolver(module);
66 let modules: Vec<OperationModule> = service
67 .operations
68 .iter()
69 .map(|operation| return operation_module(&operation.name))
70 .collect();
71
72 let mut package = Builder::new(stem);
73
74 let models = super::module_items(module, &derives)?;
75 let has_models = !models.is_empty();
76 package.add(MODELS, models)?;
77 package.add(SERVER_URLS, super::server_url_items(server_urls)?)?;
78
79 let mut declarations = Vec::with_capacity(modules.len());
80 for (operation, module) in service.operations.iter().zip(&modules) {
81 let imports = imports(OPERATION_DEPTH, has_models, false, TokenStream::new());
82 let items = operation::emit_operation_types(operation, targets, &foreign)?;
83 package.add_child(OPERATIONS, &module.stem, imports, items)?;
84 declarations.push(reexport(OPERATIONS, module));
85 }
86 package.add(OPERATIONS, block(declarations))?;
87
88 if targets.server {
89 let items = server_items(service, &modules, has_models, &mut package)?;
90 package.add(SERVER, items)?;
91 }
92 if targets.client {
93 let items = client_items(service, &modules, has_models, &mut package)?;
94 package.add(CLIENT, items)?;
95 }
96
97 return package.finish();
98}
99
100fn server_items(
102 service: &Service,
103 modules: &[OperationModule],
104 has_models: bool,
105 package: &mut Builder,
106) -> Result<Vec<TokenStream>> {
107 let items = axum::server_items(service, axum::HandlerVisibility::Parent)?;
108 let api = format_ident!("{}", axum::API_TRAIT_NAME);
109 let mut declarations = Vec::with_capacity(modules.len());
110 for ((entry, operation), module) in items.operations.into_iter().zip(&service.operations).zip(modules) {
111 let imports = imports(OPERATION_DEPTH, has_models, true, quote! { use super::#api; });
114 let mut file = entry.extractors;
115 file.push(entry.into_response);
116 file.push(entry.handler);
117 package.add_child(SERVER, &module.stem, imports, file)?;
118 let declaration = declare(SERVER, module);
119 let ident = &module.ident;
120 let handler = axum_handler_name(&operation.name).to_token();
121 declarations.push(quote! {
122 #declaration
123 use #ident::#handler;
124 });
125 }
126 let mut file: Vec<TokenStream> = imports(MODULE_DEPTH, has_models, !modules.is_empty(), TokenStream::new())
127 .into_iter()
128 .collect();
129 file.extend(block(declarations));
130 file.push(items.api_trait);
131 file.push(items.router);
132 return Ok(file);
133}
134
135fn client_items(
137 service: &Service,
138 modules: &[OperationModule],
139 has_models: bool,
140 package: &mut Builder,
141) -> Result<Vec<TokenStream>> {
142 let items = reqwest::client_items(service)?;
143 let client = format_ident!("{}", reqwest::CLIENT_STRUCT_NAME);
144 let error = format_ident!("{}", reqwest::CLIENT_ERROR_NAME);
145 let encode_set = format_ident!("{}", reqwest::ENCODE_SET_NAME);
146 let mut declarations = Vec::with_capacity(modules.len());
147 for ((method, operation), module) in items
148 .operation_methods
149 .into_iter()
150 .zip(&service.operations)
151 .zip(modules)
152 {
153 let mut extra = quote! { use super::{#client, #error}; };
156 if !operation.path_params.is_empty() {
157 extra.extend(quote! { use super::#encode_set; });
158 }
159 let imports = imports(OPERATION_DEPTH, has_models, true, extra);
160 let body = vec![quote! {
161 impl #client {
162 #method
163 }
164 }];
165 package.add_child(CLIENT, &module.stem, imports, body)?;
166 declarations.push(declare(CLIENT, module));
167 }
168 let shared = items.shared_methods;
169 let mut file = block(declarations);
170 file.push(items.error);
171 file.extend(items.encode_set);
172 file.push(items.client_struct);
173 file.push(quote! {
174 impl #client {
175 #(#shared)*
176 }
177 });
178 return Ok(file);
179}
180
181struct Builder {
183 stem: String,
185 files: Vec<GeneratedFile>,
187 mounted: Vec<&'static str>,
189}
190
191impl Builder {
192 fn new(stem: &str) -> Self {
194 return Self {
195 stem: stem.to_owned(),
196 files: Vec::new(),
197 mounted: Vec::new(),
198 };
199 }
200
201 fn add(&mut self, name: &'static str, items: Vec<TokenStream>) -> Result<()> {
207 if items.is_empty() {
208 return Ok(());
209 }
210 let path = format!("{}/{name}.rs", self.stem);
211 self.files.push(GeneratedFile::new(path, render(&items)?));
212 self.mounted.push(name);
213 return Ok(());
214 }
215
216 fn add_child(
218 &mut self,
219 parent: &'static str,
220 stem: &str,
221 imports: Option<TokenStream>,
222 items: Vec<TokenStream>,
223 ) -> Result<()> {
224 let file: Vec<TokenStream> = imports.into_iter().chain(items).collect();
225 let path = format!("{}/{parent}/{stem}.rs", self.stem);
226 self.files.push(GeneratedFile::new(path, render(&file)?));
227 return Ok(());
228 }
229
230 fn finish(mut self) -> Result<GeneratedPackage> {
232 let mounts = self.mounted.iter().map(|name| {
233 let ident = format_ident!("{name}");
234 let path = format!("{}/{name}.rs", self.stem);
235 return quote! {
236 #[path = #path]
237 mod #ident;
238 pub use #ident::*;
239 };
240 });
241 let root = render(&[quote! { #(#mounts)* }])?;
242 self.files.sort_by(|left, right| return left.path().cmp(right.path()));
243 return Ok(GeneratedPackage::new(root, self.files));
244 }
245}
246
247fn block(declarations: Vec<TokenStream>) -> Vec<TokenStream> {
253 if declarations.is_empty() {
254 return Vec::new();
255 }
256 return vec![quote! { #(#declarations)* }];
257}
258
259struct OperationModule {
266 stem: String,
268 ident: Ident,
270}
271
272fn operation_module(name: &RustIdent) -> OperationModule {
277 let text = name.logical();
278 let stem = if text == "mod" {
279 format!("{text}_")
280 } else {
281 text.to_owned()
282 };
283 return OperationModule {
284 stem,
285 ident: name.to_token(),
286 };
287}
288
289fn reexport(parent: &'static str, module: &OperationModule) -> TokenStream {
291 let declaration = declare(parent, module);
292 let ident = &module.ident;
293 return quote! {
294 #declaration
295 pub use #ident::*;
296 };
297}
298
299fn declare(parent: &'static str, module: &OperationModule) -> TokenStream {
301 let path = format!("{parent}/{}.rs", module.stem);
302 let ident = &module.ident;
303 return quote! {
304 #[path = #path]
305 mod #ident;
306 };
307}
308
309fn imports(depth: usize, models: bool, operations: bool, extra: TokenStream) -> Option<TokenStream> {
316 if !models && !operations && extra.is_empty() {
317 return None;
318 }
319 let root = root_path(depth);
320 let models = models.then(|| {
321 let ident = format_ident!("{MODELS}");
322 return quote! { use #root #ident::*; };
323 });
324 let operations = operations.then(|| {
325 let ident = format_ident!("{OPERATIONS}");
326 return quote! { use #root #ident::*; };
327 });
328 return Some(quote! {
329 #models
330 #operations
331 #extra
332 });
333}
334
335fn root_path(depth: usize) -> TokenStream {
337 let hops = std::iter::repeat_n(quote! { super:: }, depth);
338 return quote! { #(#hops)* };
339}
340
341fn render(items: &[TokenStream]) -> Result<String> {
343 let mut out = String::from(HEADER);
344 out.push_str(&render_body(items)?);
345 return Ok(out);
346}