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