1use itertools::Itertools;
2use ploidy_core::{
3 ir::{OperationView, RequestView, ResponseView},
4 parse::{Method, path::PathFragment},
5};
6use proc_macro2::{Span, TokenStream};
7use quote::{ToTokens, TokenStreamExt, format_ident, quote};
8use syn::Ident;
9
10use super::{
11 doc_attrs,
12 graph::{CodegenGraph, IdentMapping},
13 naming::CodegenIdentUsage,
14 ref_::CodegenRef,
15};
16
17pub struct CodegenOperation<'a> {
19 graph: &'a CodegenGraph<'a>,
20 op: &'a OperationView<'a, 'a>,
21}
22
23impl<'a> CodegenOperation<'a> {
24 pub fn new(graph: &'a CodegenGraph<'a>, op: &'a OperationView<'a, 'a>) -> Self {
25 Self { graph, op }
26 }
27
28 fn url(&self) -> TokenStream {
31 let segments = self
32 .op
33 .path()
34 .segments()
35 .map(|segment| match segment.fragments() {
36 [] => quote! { "" },
37 [PathFragment::Literal(text)] => quote! { #text },
38 [PathFragment::Param(name)] => {
39 let param = CodegenIdentUsage::Param(
40 self.graph.ident(IdentMapping::Path(self.op.id(), name)),
41 );
42 quote!(#param)
43 }
44 fragments => {
45 let format = fragments.iter().fold(String::new(), |mut f, fragment| {
47 match fragment {
48 PathFragment::Literal(text) => {
49 f.push_str(&text.replace('{', "{{").replace('}', "}}"))
50 }
51 PathFragment::Param(_) => f.push_str("{}"),
52 }
53 f
54 });
55 let args = fragments
56 .iter()
57 .filter_map(|fragment| match fragment {
58 PathFragment::Param(name) => Some(name),
59 PathFragment::Literal(_) => None,
60 })
61 .map(|name| {
62 let param = CodegenIdentUsage::Param(
66 self.graph.ident(IdentMapping::Path(self.op.id(), name)),
67 );
68 quote!(#param)
69 });
70 quote! { &format!(#format, #(#args),*) }
71 }
72 });
73 let query = self
74 .op
75 .path()
76 .query()
77 .map(|param| {
78 let name = param.name;
79 let value = param.value;
80 quote! { .append_pair(#name, #value) }
81 })
82 .reduce(|a, b| quote!(#a #b))
83 .map(|pairs| {
84 quote! {
85 url.query_pairs_mut()
86 #pairs;
87 }
88 });
89
90 quote! {
91 let url = {
92 let mut url = self.base_url.clone();
93 let _ = url
94 .path_segments_mut()
95 .map(|mut segments| {
96 segments.pop_if_empty()
97 #(.push(#segments))*;
98 });
99 #query
100 url
101 };
102 }
103 }
104
105 fn query(&self) -> Option<TokenStream> {
107 self.op.query().next().is_some().then(|| {
108 let query_name = format_ident!(
109 "{}Query",
110 CodegenIdentUsage::Type(self.graph.ident(self.op.id()))
111 );
112 quote! {
113 let url = ::ploidy_util::serde::Serialize::serialize(
114 query,
115 ::ploidy_util::QuerySerializer::new(
116 url,
117 parameters::#query_name::STYLES,
118 ),
119 )?;
120 }
121 })
122 }
123}
124
125impl ToTokens for CodegenOperation<'_> {
126 fn to_tokens(&self, tokens: &mut TokenStream) {
127 let mut params = vec![];
128
129 let paths = self.op.path().params().collect_vec();
130 for param in &paths {
131 let param = CodegenIdentUsage::Param(
132 self.graph
133 .ident(IdentMapping::Path(self.op.id(), param.name())),
134 );
135 params.push(quote! { #param: &str });
136 }
137
138 if self.op.query().next().is_some() {
139 let query_type_name = format_ident!(
142 "{}Query",
143 CodegenIdentUsage::Type(self.graph.ident(self.op.id()))
144 );
145 params.push(quote! { query: ¶meters::#query_type_name });
146 }
147
148 if let Some(request) = self.op.request() {
149 match request {
150 RequestView::Json(view) => {
151 let param_type = CodegenRef::new(self.graph, &view);
152 params.push(quote! { request: impl Into<#param_type> });
153 }
154 RequestView::Multipart => {
155 params.push(quote! { form: crate::util::reqwest::multipart::Form });
156 }
157 }
158 }
159
160 let return_type = match self.op.response() {
161 Some(response) => match response {
162 ResponseView::Json(view) => CodegenRef::new(self.graph, &view).into_token_stream(),
163 },
164 None => quote! { () },
165 };
166
167 let build_url = self.url();
168
169 let build_query = self.query();
170
171 let http_method = CodegenMethod(self.op.method());
172
173 let build_request = match self.op.request() {
174 Some(RequestView::Json(_)) => quote! {
175 let response = self.client
176 .#http_method(url)
177 .headers(self.headers.clone())
178 .json(&request.into())
179 .send()
180 .await?
181 .error_for_status()?;
182 },
183 Some(RequestView::Multipart) => quote! {
184 let response = self.client
185 .#http_method(url)
186 .headers(self.headers.clone())
187 .multipart(form)
188 .send()
189 .await?
190 .error_for_status()?;
191 },
192 None => quote! {
193 let response = self.client
194 .#http_method(url)
195 .headers(self.headers.clone())
196 .send()
197 .await?
198 .error_for_status()?;
199 },
200 };
201
202 let parse_response = if self.op.response().is_some() {
203 quote! {
204 let body = response.bytes().await?;
205 let deserializer = &mut ::ploidy_util::serde_json::Deserializer::from_slice(&body);
206 let result = ::ploidy_util::serde_path_to_error::deserialize(deserializer)
207 .map_err(crate::error::JsonError::from)?;
208 Ok(result)
209 }
210 } else {
211 quote! {
212 let _ = response;
213 Ok(())
214 }
215 };
216
217 let method_name = CodegenIdentUsage::Method(self.graph.ident(self.op.id()));
218
219 let doc = {
220 let url = format!(" {} {}", self.op.method().as_str(), self.op.path());
221 match self.op.description() {
222 Some(description) => {
223 let attrs = doc_attrs(description);
224 quote! {
225 #attrs
226 #[doc = ""]
227 #[doc = #url]
228 }
229 }
230 None => {
231 quote!(#[doc = #url])
232 }
233 }
234 };
235
236 tokens.append_all(quote! {
237 #doc
238 pub async fn #method_name(
239 &self,
240 #(#params),*
241 ) -> Result<#return_type, crate::error::Error> {
242 #build_url
243 #build_query
244 #build_request
245 #parse_response
246 }
247 });
248 }
249}
250
251#[derive(Clone, Copy, Debug)]
252pub struct CodegenMethod(pub Method);
253
254impl ToTokens for CodegenMethod {
255 fn to_tokens(&self, tokens: &mut TokenStream) {
256 tokens.append(match self.0 {
257 Method::Get => Ident::new("get", Span::call_site()),
258 Method::Post => Ident::new("post", Span::call_site()),
259 Method::Put => Ident::new("put", Span::call_site()),
260 Method::Patch => Ident::new("patch", Span::call_site()),
261 Method::Delete => Ident::new("delete", Span::call_site()),
262 });
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 use ploidy_core::{
271 arena::Arena,
272 ir::{RawGraph, Spec},
273 parse::Document,
274 };
275 use pretty_assertions::assert_eq;
276 use syn::parse_quote;
277
278 use crate::CodegenGraph;
279
280 #[test]
283 fn test_operation_with_path_and_query_params() {
284 let doc = Document::from_yaml(indoc::indoc! {"
285 openapi: 3.0.0
286 info:
287 title: Test API
288 version: 1.0.0
289 paths:
290 /items/{item_id}:
291 get:
292 operationId: getItem
293 description: Gets an item.
294 parameters:
295 - name: item_id
296 in: path
297 required: true
298 schema:
299 type: string
300 - name: expand
301 in: query
302 schema:
303 type: boolean
304 responses:
305 '200':
306 description: OK
307 "})
308 .unwrap();
309
310 let arena = Arena::new();
311 let spec = Spec::from_doc(&arena, &doc).unwrap();
312 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
313
314 let op = graph.operations().next().unwrap();
315 let codegen = CodegenOperation::new(&graph, &op);
316
317 let actual: syn::ImplItemFn = parse_quote!(#codegen);
318 let expected: syn::ImplItemFn = parse_quote! {
319 #[doc = " Gets an item."]
320 #[doc = ""]
321 #[doc = " GET /items/{item_id}"]
322 pub async fn get_item(
323 &self,
324 item_id: &str,
325 query: ¶meters::GetItemQuery
326 ) -> Result<(), crate::error::Error> {
327 let url = {
328 let mut url = self.base_url.clone();
329 let _ = url
330 .path_segments_mut()
331 .map(|mut segments| {
332 segments.pop_if_empty()
333 .push("items")
334 .push(item_id);
335 });
336 url
337 };
338 let url = ::ploidy_util::serde::Serialize::serialize(
339 query,
340 ::ploidy_util::QuerySerializer::new(
341 url,
342 parameters::GetItemQuery::STYLES,
343 ),
344 )?;
345 let response = self
346 .client
347 .get(url)
348 .headers(self.headers.clone())
349 .send()
350 .await?
351 .error_for_status()?;
352 let _ = response;
353 Ok(())
354 }
355 };
356 assert_eq!(actual, expected);
357 }
358
359 #[test]
360 fn test_operation_with_query_params_only() {
361 let doc = Document::from_yaml(indoc::indoc! {"
362 openapi: 3.0.0
363 info:
364 title: Test API
365 version: 1.0.0
366 paths:
367 /items:
368 get:
369 operationId: getItems
370 parameters:
371 - name: limit
372 in: query
373 schema:
374 type: integer
375 format: int32
376 responses:
377 '200':
378 description: OK
379 "})
380 .unwrap();
381
382 let arena = Arena::new();
383 let spec = Spec::from_doc(&arena, &doc).unwrap();
384 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
385
386 let op = graph.operations().next().unwrap();
387 let codegen = CodegenOperation::new(&graph, &op);
388
389 let actual: syn::ImplItemFn = parse_quote!(#codegen);
390 let expected: syn::ImplItemFn = parse_quote! {
391 #[doc = " GET /items"]
392 pub async fn get_items(
393 &self,
394 query: ¶meters::GetItemsQuery
395 ) -> Result<(), crate::error::Error> {
396 let url = {
397 let mut url = self.base_url.clone();
398 let _ = url
399 .path_segments_mut()
400 .map(|mut segments| {
401 segments.pop_if_empty()
402 .push("items");
403 });
404 url
405 };
406 let url = ::ploidy_util::serde::Serialize::serialize(
407 query,
408 ::ploidy_util::QuerySerializer::new(
409 url,
410 parameters::GetItemsQuery::STYLES,
411 ),
412 )?;
413 let response = self
414 .client
415 .get(url)
416 .headers(self.headers.clone())
417 .send()
418 .await?
419 .error_for_status()?;
420 let _ = response;
421 Ok(())
422 }
423 };
424 assert_eq!(actual, expected);
425 }
426
427 #[test]
428 fn test_path_param_named_query_does_not_shadow() {
429 let doc = Document::from_yaml(indoc::indoc! {"
430 openapi: 3.0.0
431 info:
432 title: Test API
433 version: 1.0.0
434 paths:
435 /search/{query}:
436 get:
437 operationId: search
438 parameters:
439 - name: query
440 in: path
441 required: true
442 schema:
443 type: string
444 - name: limit
445 in: query
446 schema:
447 type: integer
448 format: int32
449 responses:
450 '200':
451 description: OK
452 "})
453 .unwrap();
454
455 let arena = Arena::new();
456 let spec = Spec::from_doc(&arena, &doc).unwrap();
457 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
458
459 let op = graph.operations().next().unwrap();
460 let codegen = CodegenOperation::new(&graph, &op);
461
462 let actual: syn::ImplItemFn = parse_quote!(#codegen);
463 let expected: syn::ImplItemFn = parse_quote! {
464 #[doc = " GET /search/{query}"]
465 pub async fn search(
466 &self,
467 query_2: &str,
468 query: ¶meters::SearchQuery
469 ) -> Result<(), crate::error::Error> {
470 let url = {
471 let mut url = self.base_url.clone();
472 let _ = url
473 .path_segments_mut()
474 .map(|mut segments| {
475 segments.pop_if_empty()
476 .push("search")
477 .push(query_2);
478 });
479 url
480 };
481 let url = ::ploidy_util::serde::Serialize::serialize(
482 query,
483 ::ploidy_util::QuerySerializer::new(
484 url,
485 parameters::SearchQuery::STYLES,
486 ),
487 )?;
488 let response = self
489 .client
490 .get(url)
491 .headers(self.headers.clone())
492 .send()
493 .await?
494 .error_for_status()?;
495 let _ = response;
496 Ok(())
497 }
498 };
499 assert_eq!(actual, expected);
500 }
501
502 #[test]
505 fn test_operation_with_query_params_and_request_body() {
506 let doc = Document::from_yaml(indoc::indoc! {"
507 openapi: 3.0.0
508 info:
509 title: Test API
510 version: 1.0.0
511 paths:
512 /items/{item_id}:
513 put:
514 operationId: updateItem
515 parameters:
516 - name: item_id
517 in: path
518 required: true
519 schema:
520 type: string
521 - name: dry_run
522 in: query
523 schema:
524 type: boolean
525 requestBody:
526 content:
527 application/json:
528 schema:
529 $ref: '#/components/schemas/Item'
530 responses:
531 '200':
532 description: OK
533 content:
534 application/json:
535 schema:
536 $ref: '#/components/schemas/Item'
537 components:
538 schemas:
539 Item:
540 type: object
541 properties:
542 name:
543 type: string
544 "})
545 .unwrap();
546
547 let arena = Arena::new();
548 let spec = Spec::from_doc(&arena, &doc).unwrap();
549 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
550
551 let op = graph.operations().next().unwrap();
552 let codegen = CodegenOperation::new(&graph, &op);
553
554 let actual: syn::ImplItemFn = parse_quote!(#codegen);
555 let expected: syn::ImplItemFn = parse_quote! {
556 #[doc = " PUT /items/{item_id}"]
557 pub async fn update_item(
558 &self,
559 item_id: &str,
560 query: ¶meters::UpdateItemQuery,
561 request: impl Into<crate::types::Item>
562 ) -> Result<crate::types::Item, crate::error::Error> {
563 let url = {
564 let mut url = self.base_url.clone();
565 let _ = url
566 .path_segments_mut()
567 .map(|mut segments| {
568 segments.pop_if_empty()
569 .push("items")
570 .push(item_id);
571 });
572 url
573 };
574 let url = ::ploidy_util::serde::Serialize::serialize(
575 query,
576 ::ploidy_util::QuerySerializer::new(
577 url,
578 parameters::UpdateItemQuery::STYLES,
579 ),
580 )?;
581 let response = self
582 .client
583 .put(url)
584 .headers(self.headers.clone())
585 .json(&request.into())
586 .send()
587 .await?
588 .error_for_status()?;
589 let body = response.bytes().await?;
590 let deserializer = &mut ::ploidy_util::serde_json::Deserializer::from_slice(&body);
591 let result = ::ploidy_util::serde_path_to_error::deserialize(deserializer)
592 .map_err(crate::error::JsonError::from)?;
593 Ok(result)
594 }
595 };
596 assert_eq!(actual, expected);
597 }
598
599 #[test]
602 fn test_operation_without_query_params() {
603 let doc = Document::from_yaml(indoc::indoc! {"
604 openapi: 3.0.0
605 info:
606 title: Test API
607 version: 1.0.0
608 paths:
609 /items/{item_id}:
610 get:
611 operationId: getItem
612 parameters:
613 - name: item_id
614 in: path
615 required: true
616 schema:
617 type: string
618 responses:
619 '200':
620 description: OK
621 "})
622 .unwrap();
623
624 let arena = Arena::new();
625 let spec = Spec::from_doc(&arena, &doc).unwrap();
626 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
627
628 let op = graph.operations().next().unwrap();
629 let codegen = CodegenOperation::new(&graph, &op);
630
631 let actual: syn::ImplItemFn = parse_quote!(#codegen);
632 let expected: syn::ImplItemFn = parse_quote! {
633 #[doc = " GET /items/{item_id}"]
634 pub async fn get_item(
635 &self,
636 item_id: &str
637 ) -> Result<(), crate::error::Error> {
638 let url = {
639 let mut url = self.base_url.clone();
640 let _ = url
641 .path_segments_mut()
642 .map(|mut segments| {
643 segments.pop_if_empty()
644 .push("items")
645 .push(item_id);
646 });
647 url
648 };
649 let response = self
650 .client
651 .get(url)
652 .headers(self.headers.clone())
653 .send()
654 .await?
655 .error_for_status()?;
656 let _ = response;
657 Ok(())
658 }
659 };
660 assert_eq!(actual, expected);
661 }
662
663 #[test]
666 fn test_operation_with_synthesized_path_param() {
667 let doc = Document::from_yaml(indoc::indoc! {"
668 openapi: 3.0.0
669 info:
670 title: Test API
671 version: 1.0.0
672 paths:
673 /items/{item_id}:
674 get:
675 operationId: getItem
676 responses:
677 '200':
678 description: OK
679 "})
680 .unwrap();
681
682 let arena = Arena::new();
683 let spec = Spec::from_doc(&arena, &doc).unwrap();
684 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
685
686 let op = graph.operations().next().unwrap();
687 let codegen = CodegenOperation::new(&graph, &op);
688
689 let actual: syn::ImplItemFn = parse_quote!(#codegen);
690 let expected: syn::ImplItemFn = parse_quote! {
691 #[doc = " GET /items/{item_id}"]
692 pub async fn get_item(
693 &self,
694 item_id: &str
695 ) -> Result<(), crate::error::Error> {
696 let url = {
697 let mut url = self.base_url.clone();
698 let _ = url
699 .path_segments_mut()
700 .map(|mut segments| {
701 segments.pop_if_empty()
702 .push("items")
703 .push(item_id);
704 });
705 url
706 };
707 let response = self
708 .client
709 .get(url)
710 .headers(self.headers.clone())
711 .send()
712 .await?
713 .error_for_status()?;
714 let _ = response;
715 Ok(())
716 }
717 };
718 assert_eq!(actual, expected);
719 }
720
721 #[test]
724 fn test_operation_with_literal_query_params() {
725 let doc = Document::from_yaml(indoc::indoc! {"
726 openapi: 3.0.0
727 info:
728 title: Test API
729 version: 1.0.0
730 paths:
731 /v1/messages?beta=true&expand:
732 post:
733 operationId: betaCreateMessage
734 requestBody:
735 content:
736 application/json:
737 schema:
738 $ref: '#/components/schemas/Message'
739 responses:
740 '200':
741 description: OK
742 content:
743 application/json:
744 schema:
745 $ref: '#/components/schemas/Message'
746 components:
747 schemas:
748 Message:
749 type: object
750 properties:
751 content:
752 type: string
753 "})
754 .unwrap();
755
756 let arena = Arena::new();
757 let spec = Spec::from_doc(&arena, &doc).unwrap();
758 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
759
760 let op = graph.operations().next().unwrap();
761 let codegen = CodegenOperation::new(&graph, &op);
762
763 let actual: syn::ImplItemFn = parse_quote!(#codegen);
764 let expected: syn::ImplItemFn = parse_quote! {
765 #[doc = " POST /v1/messages?beta=true&expand="]
766 pub async fn beta_create_message(
767 &self,
768 request: impl Into<crate::types::Message>
769 ) -> Result<crate::types::Message, crate::error::Error> {
770 let url = {
771 let mut url = self.base_url.clone();
772 let _ = url
773 .path_segments_mut()
774 .map(|mut segments| {
775 segments.pop_if_empty()
776 .push("v1")
777 .push("messages");
778 });
779 url.query_pairs_mut()
780 .append_pair("beta", "true")
781 .append_pair("expand", "");
782 url
783 };
784 let response = self
785 .client
786 .post(url)
787 .headers(self.headers.clone())
788 .json(&request.into())
789 .send()
790 .await?
791 .error_for_status()?;
792 let body = response.bytes().await?;
793 let deserializer = &mut ::ploidy_util::serde_json::Deserializer::from_slice(&body);
794 let result = ::ploidy_util::serde_path_to_error::deserialize(deserializer)
795 .map_err(crate::error::JsonError::from)?;
796 Ok(result)
797 }
798 };
799 assert_eq!(actual, expected);
800 }
801
802 #[test]
803 fn test_operation_with_literal_and_declared_query_params() {
804 let doc = Document::from_yaml(indoc::indoc! {"
805 openapi: 3.0.0
806 info:
807 title: Test API
808 version: 1.0.0
809 paths:
810 /v1/messages?beta=true:
811 post:
812 operationId: betaCreateMessage
813 parameters:
814 - name: limit
815 in: query
816 schema:
817 type: integer
818 format: int32
819 requestBody:
820 content:
821 application/json:
822 schema:
823 $ref: '#/components/schemas/Message'
824 responses:
825 '200':
826 description: OK
827 content:
828 application/json:
829 schema:
830 $ref: '#/components/schemas/Message'
831 components:
832 schemas:
833 Message:
834 type: object
835 properties:
836 content:
837 type: string
838 "})
839 .unwrap();
840
841 let arena = Arena::new();
842 let spec = Spec::from_doc(&arena, &doc).unwrap();
843 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
844
845 let op = graph.operations().next().unwrap();
846 let codegen = CodegenOperation::new(&graph, &op);
847
848 let actual: syn::ImplItemFn = parse_quote!(#codegen);
849 let expected: syn::ImplItemFn = parse_quote! {
850 #[doc = " POST /v1/messages?beta=true"]
851 pub async fn beta_create_message(
852 &self,
853 query: ¶meters::BetaCreateMessageQuery,
854 request: impl Into<crate::types::Message>
855 ) -> Result<crate::types::Message, crate::error::Error> {
856 let url = {
857 let mut url = self.base_url.clone();
858 let _ = url
859 .path_segments_mut()
860 .map(|mut segments| {
861 segments.pop_if_empty()
862 .push("v1")
863 .push("messages");
864 });
865 url.query_pairs_mut()
866 .append_pair("beta", "true");
867 url
868 };
869 let url = ::ploidy_util::serde::Serialize::serialize(
870 query,
871 ::ploidy_util::QuerySerializer::new(
872 url,
873 parameters::BetaCreateMessageQuery::STYLES,
874 ),
875 )?;
876 let response = self
877 .client
878 .post(url)
879 .headers(self.headers.clone())
880 .json(&request.into())
881 .send()
882 .await?
883 .error_for_status()?;
884 let body = response.bytes().await?;
885 let deserializer = &mut ::ploidy_util::serde_json::Deserializer::from_slice(&body);
886 let result = ::ploidy_util::serde_path_to_error::deserialize(deserializer)
887 .map_err(crate::error::JsonError::from)?;
888 Ok(result)
889 }
890 };
891 assert_eq!(actual, expected);
892 }
893
894 #[test]
895 fn test_operation_with_path_params_and_literal_and_declared_query_params() {
896 let doc = Document::from_yaml(indoc::indoc! {"
897 openapi: 3.0.0
898 info:
899 title: Test API
900 version: 1.0.0
901 paths:
902 /v1/models/{model_id}?beta=true:
903 get:
904 operationId: betaGetModel
905 parameters:
906 - name: model_id
907 in: path
908 required: true
909 schema:
910 type: string
911 - name: expand
912 in: query
913 schema:
914 type: boolean
915 responses:
916 '200':
917 description: OK
918 content:
919 application/json:
920 schema:
921 $ref: '#/components/schemas/Model'
922 components:
923 schemas:
924 Model:
925 type: object
926 properties:
927 id:
928 type: string
929 "})
930 .unwrap();
931
932 let arena = Arena::new();
933 let spec = Spec::from_doc(&arena, &doc).unwrap();
934 let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
935
936 let op = graph.operations().next().unwrap();
937 let codegen = CodegenOperation::new(&graph, &op);
938
939 let actual: syn::ImplItemFn = parse_quote!(#codegen);
940 let expected: syn::ImplItemFn = parse_quote! {
941 #[doc = " GET /v1/models/{model_id}?beta=true"]
942 pub async fn beta_get_model(
943 &self,
944 model_id: &str,
945 query: ¶meters::BetaGetModelQuery
946 ) -> Result<crate::types::Model, crate::error::Error> {
947 let url = {
948 let mut url = self.base_url.clone();
949 let _ = url
950 .path_segments_mut()
951 .map(|mut segments| {
952 segments.pop_if_empty()
953 .push("v1")
954 .push("models")
955 .push(model_id);
956 });
957 url.query_pairs_mut()
958 .append_pair("beta", "true");
959 url
960 };
961 let url = ::ploidy_util::serde::Serialize::serialize(
962 query,
963 ::ploidy_util::QuerySerializer::new(
964 url,
965 parameters::BetaGetModelQuery::STYLES,
966 ),
967 )?;
968 let response = self
969 .client
970 .get(url)
971 .headers(self.headers.clone())
972 .send()
973 .await?
974 .error_for_status()?;
975 let body = response.bytes().await?;
976 let deserializer = &mut ::ploidy_util::serde_json::Deserializer::from_slice(&body);
977 let result = ::ploidy_util::serde_path_to_error::deserialize(deserializer)
978 .map_err(crate::error::JsonError::from)?;
979 Ok(result)
980 }
981 };
982 assert_eq!(actual, expected);
983 }
984}