Skip to main content

ploidy_codegen_rust/
primitive.rs

1use ploidy_core::ir::{ExtendableView, PrimitiveType, PrimitiveView};
2use proc_macro2::TokenStream;
3use quote::{ToTokens, TokenStreamExt, quote};
4
5use super::config::DateTimeFormat;
6
7#[derive(Clone, Copy, Debug)]
8pub struct CodegenPrimitive<'a> {
9    ty: &'a PrimitiveView<'a>,
10}
11
12impl<'a> CodegenPrimitive<'a> {
13    pub fn new(ty: &'a PrimitiveView<'a>) -> Self {
14        Self { ty }
15    }
16}
17
18impl<'a> ToTokens for CodegenPrimitive<'a> {
19    fn to_tokens(&self, tokens: &mut TokenStream) {
20        tokens.append_all(match self.ty.ty() {
21            PrimitiveType::String => quote! { ::std::string::String },
22            PrimitiveType::I8 => quote! { i8 },
23            PrimitiveType::U8 => quote! { u8 },
24            PrimitiveType::I16 => quote! { i16 },
25            PrimitiveType::U16 => quote! { u16 },
26            PrimitiveType::I32 => quote! { i32 },
27            PrimitiveType::U32 => quote! { u32 },
28            PrimitiveType::I64 => quote! { i64 },
29            PrimitiveType::U64 => quote! { u64 },
30            PrimitiveType::F32 => quote! { f32 },
31            PrimitiveType::F64 => quote! { f64 },
32            PrimitiveType::Bool => quote! { bool },
33            PrimitiveType::DateTime => {
34                let format = self
35                    .ty
36                    .extensions()
37                    .get::<DateTimeFormat>()
38                    .as_deref()
39                    .copied()
40                    .unwrap_or_default();
41                match format {
42                    DateTimeFormat::Rfc3339 => {
43                        quote! { ::ploidy_util::chrono::DateTime<::ploidy_util::chrono::Utc> }
44                    }
45                    DateTimeFormat::UnixSeconds => {
46                        quote! { ::ploidy_util::date_time::UnixSeconds }
47                    }
48                    DateTimeFormat::UnixMilliseconds => {
49                        quote! { ::ploidy_util::date_time::UnixMilliseconds }
50                    }
51                    DateTimeFormat::UnixMicroseconds => {
52                        quote! { ::ploidy_util::date_time::UnixMicroseconds }
53                    }
54                    DateTimeFormat::UnixNanoseconds => {
55                        quote! { ::ploidy_util::date_time::UnixNanoseconds }
56                    }
57                }
58            }
59            PrimitiveType::UnixTime => quote! { ::ploidy_util::date_time::UnixSeconds },
60            PrimitiveType::Date => quote! { ::ploidy_util::chrono::NaiveDate },
61            PrimitiveType::Url => quote! { ::ploidy_util::url::Url },
62            PrimitiveType::Uuid => quote! { ::ploidy_util::uuid::Uuid },
63            PrimitiveType::Bytes => quote! { ::ploidy_util::binary::Base64 },
64            PrimitiveType::Binary => quote! { ::ploidy_util::serde_bytes::ByteBuf },
65        });
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    use itertools::Itertools;
74    use ploidy_core::{
75        arena::Arena,
76        ir::{RawGraph, Spec},
77        parse::Document,
78    };
79    use pretty_assertions::assert_eq;
80    use syn::parse_quote;
81
82    use crate::{CodegenConfig, CodegenGraph, DateTimeFormat};
83
84    #[test]
85    fn test_codegen_primitive_string() {
86        let doc = Document::from_yaml(indoc::indoc! {"
87            openapi: 3.0.0
88            info:
89              title: Test
90              version: 1.0.0
91            paths: {}
92            components:
93              schemas:
94                Test:
95                  type: string
96        "})
97        .unwrap();
98        let arena = Arena::new();
99        let spec = Spec::from_doc(&arena, &doc).unwrap();
100        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
101        let primitives = graph.primitives().collect_vec();
102        let [ty] = &*primitives else {
103            panic!("expected string; got `{primitives:?}`");
104        };
105        let p = CodegenPrimitive::new(ty);
106        let actual: syn::Type = parse_quote!(#p);
107        let expected: syn::Type = parse_quote!(::std::string::String);
108        assert_eq!(actual, expected);
109    }
110
111    #[test]
112    fn test_codegen_primitive_i8() {
113        let doc = Document::from_yaml(indoc::indoc! {"
114            openapi: 3.0.0
115            info:
116              title: Test
117              version: 1.0.0
118            paths: {}
119            components:
120              schemas:
121                Test:
122                  type: object
123                  required: [value]
124                  properties:
125                    value:
126                      type: integer
127                      format: int8
128        "})
129        .unwrap();
130        let arena = Arena::new();
131        let spec = Spec::from_doc(&arena, &doc).unwrap();
132        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
133        let primitives = graph.primitives().collect_vec();
134        let [ty] = &*primitives else {
135            panic!("expected i8; got `{primitives:?}`");
136        };
137        let p = CodegenPrimitive::new(ty);
138        let actual: syn::Type = parse_quote!(#p);
139        let expected: syn::Type = parse_quote!(i8);
140        assert_eq!(actual, expected);
141    }
142
143    #[test]
144    fn test_codegen_primitive_u8() {
145        let doc = Document::from_yaml(indoc::indoc! {"
146            openapi: 3.0.0
147            info:
148              title: Test
149              version: 1.0.0
150            paths: {}
151            components:
152              schemas:
153                Test:
154                  type: object
155                  required: [value]
156                  properties:
157                    value:
158                      type: integer
159                      format: uint8
160        "})
161        .unwrap();
162        let arena = Arena::new();
163        let spec = Spec::from_doc(&arena, &doc).unwrap();
164        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
165        let primitives = graph.primitives().collect_vec();
166        let [ty] = &*primitives else {
167            panic!("expected u8; got `{primitives:?}`");
168        };
169        let p = CodegenPrimitive::new(ty);
170        let actual: syn::Type = parse_quote!(#p);
171        let expected: syn::Type = parse_quote!(u8);
172        assert_eq!(actual, expected);
173    }
174
175    #[test]
176    fn test_codegen_primitive_i16() {
177        let doc = Document::from_yaml(indoc::indoc! {"
178            openapi: 3.0.0
179            info:
180              title: Test
181              version: 1.0.0
182            paths: {}
183            components:
184              schemas:
185                Test:
186                  type: object
187                  required: [value]
188                  properties:
189                    value:
190                      type: integer
191                      format: int16
192        "})
193        .unwrap();
194        let arena = Arena::new();
195        let spec = Spec::from_doc(&arena, &doc).unwrap();
196        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
197        let primitives = graph.primitives().collect_vec();
198        let [ty] = &*primitives else {
199            panic!("expected i16; got `{primitives:?}`");
200        };
201        let p = CodegenPrimitive::new(ty);
202        let actual: syn::Type = parse_quote!(#p);
203        let expected: syn::Type = parse_quote!(i16);
204        assert_eq!(actual, expected);
205    }
206
207    #[test]
208    fn test_codegen_primitive_u16() {
209        let doc = Document::from_yaml(indoc::indoc! {"
210            openapi: 3.0.0
211            info:
212              title: Test
213              version: 1.0.0
214            paths: {}
215            components:
216              schemas:
217                Test:
218                  type: object
219                  required: [value]
220                  properties:
221                    value:
222                      type: integer
223                      format: uint16
224        "})
225        .unwrap();
226        let arena = Arena::new();
227        let spec = Spec::from_doc(&arena, &doc).unwrap();
228        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
229        let primitives = graph.primitives().collect_vec();
230        let [ty] = &*primitives else {
231            panic!("expected u16; got `{primitives:?}`");
232        };
233        let p = CodegenPrimitive::new(ty);
234        let actual: syn::Type = parse_quote!(#p);
235        let expected: syn::Type = parse_quote!(u16);
236        assert_eq!(actual, expected);
237    }
238
239    #[test]
240    fn test_codegen_primitive_i32() {
241        let doc = Document::from_yaml(indoc::indoc! {"
242            openapi: 3.0.0
243            info:
244              title: Test
245              version: 1.0.0
246            paths: {}
247            components:
248              schemas:
249                Test:
250                  type: object
251                  required: [value]
252                  properties:
253                    value:
254                      type: integer
255                      format: int32
256        "})
257        .unwrap();
258        let arena = Arena::new();
259        let spec = Spec::from_doc(&arena, &doc).unwrap();
260        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
261        let primitives = graph.primitives().collect_vec();
262        let [ty] = &*primitives else {
263            panic!("expected string; got `{primitives:?}`");
264        };
265        let p = CodegenPrimitive::new(ty);
266        let actual: syn::Type = parse_quote!(#p);
267        let expected: syn::Type = parse_quote!(i32);
268        assert_eq!(actual, expected);
269    }
270
271    #[test]
272    fn test_codegen_primitive_u32() {
273        let doc = Document::from_yaml(indoc::indoc! {"
274            openapi: 3.0.0
275            info:
276              title: Test
277              version: 1.0.0
278            paths: {}
279            components:
280              schemas:
281                Test:
282                  type: object
283                  required: [value]
284                  properties:
285                    value:
286                      type: integer
287                      format: uint32
288        "})
289        .unwrap();
290        let arena = Arena::new();
291        let spec = Spec::from_doc(&arena, &doc).unwrap();
292        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
293        let primitives = graph.primitives().collect_vec();
294        let [ty] = &*primitives else {
295            panic!("expected u32; got `{primitives:?}`");
296        };
297        let p = CodegenPrimitive::new(ty);
298        let actual: syn::Type = parse_quote!(#p);
299        let expected: syn::Type = parse_quote!(u32);
300        assert_eq!(actual, expected);
301    }
302
303    #[test]
304    fn test_codegen_primitive_i64() {
305        let doc = Document::from_yaml(indoc::indoc! {"
306            openapi: 3.0.0
307            info:
308              title: Test
309              version: 1.0.0
310            paths: {}
311            components:
312              schemas:
313                Test:
314                  type: object
315                  required: [value]
316                  properties:
317                    value:
318                      type: integer
319                      format: int64
320        "})
321        .unwrap();
322        let arena = Arena::new();
323        let spec = Spec::from_doc(&arena, &doc).unwrap();
324        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
325        let primitives = graph.primitives().collect_vec();
326        let [ty] = &*primitives else {
327            panic!("expected i64; got `{primitives:?}`");
328        };
329        let p = CodegenPrimitive::new(ty);
330        let actual: syn::Type = parse_quote!(#p);
331        let expected: syn::Type = parse_quote!(i64);
332        assert_eq!(actual, expected);
333    }
334
335    #[test]
336    fn test_codegen_primitive_u64() {
337        let doc = Document::from_yaml(indoc::indoc! {"
338            openapi: 3.0.0
339            info:
340              title: Test
341              version: 1.0.0
342            paths: {}
343            components:
344              schemas:
345                Test:
346                  type: object
347                  required: [value]
348                  properties:
349                    value:
350                      type: integer
351                      format: uint64
352        "})
353        .unwrap();
354        let arena = Arena::new();
355        let spec = Spec::from_doc(&arena, &doc).unwrap();
356        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
357        let primitives = graph.primitives().collect_vec();
358        let [ty] = &*primitives else {
359            panic!("expected u64; got `{primitives:?}`");
360        };
361        let p = CodegenPrimitive::new(ty);
362        let actual: syn::Type = parse_quote!(#p);
363        let expected: syn::Type = parse_quote!(u64);
364        assert_eq!(actual, expected);
365    }
366
367    #[test]
368    fn test_codegen_primitive_f32() {
369        let doc = Document::from_yaml(indoc::indoc! {"
370            openapi: 3.0.0
371            info:
372              title: Test
373              version: 1.0.0
374            paths: {}
375            components:
376              schemas:
377                Test:
378                  type: object
379                  required: [value]
380                  properties:
381                    value:
382                      type: number
383                      format: float
384        "})
385        .unwrap();
386        let arena = Arena::new();
387        let spec = Spec::from_doc(&arena, &doc).unwrap();
388        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
389        let primitives = graph.primitives().collect_vec();
390        let [ty] = &*primitives else {
391            panic!("expected f32; got `{primitives:?}`");
392        };
393        let p = CodegenPrimitive::new(ty);
394        let actual: syn::Type = parse_quote!(#p);
395        let expected: syn::Type = parse_quote!(f32);
396        assert_eq!(actual, expected);
397    }
398
399    #[test]
400    fn test_codegen_primitive_f64() {
401        let doc = Document::from_yaml(indoc::indoc! {"
402            openapi: 3.0.0
403            info:
404              title: Test
405              version: 1.0.0
406            paths: {}
407            components:
408              schemas:
409                Test:
410                  type: object
411                  required: [value]
412                  properties:
413                    value:
414                      type: number
415                      format: double
416        "})
417        .unwrap();
418        let arena = Arena::new();
419        let spec = Spec::from_doc(&arena, &doc).unwrap();
420        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
421        let primitives = graph.primitives().collect_vec();
422        let [ty] = &*primitives else {
423            panic!("expected f64; got `{primitives:?}`");
424        };
425        let p = CodegenPrimitive::new(ty);
426        let actual: syn::Type = parse_quote!(#p);
427        let expected: syn::Type = parse_quote!(f64);
428        assert_eq!(actual, expected);
429    }
430
431    #[test]
432    fn test_codegen_primitive_bool() {
433        let doc = Document::from_yaml(indoc::indoc! {"
434            openapi: 3.0.0
435            info:
436              title: Test
437              version: 1.0.0
438            paths: {}
439            components:
440              schemas:
441                Test:
442                  type: object
443                  required: [value]
444                  properties:
445                    value:
446                      type: boolean
447        "})
448        .unwrap();
449        let arena = Arena::new();
450        let spec = Spec::from_doc(&arena, &doc).unwrap();
451        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
452        let primitives = graph.primitives().collect_vec();
453        let [ty] = &*primitives else {
454            panic!("expected bool; got `{primitives:?}`");
455        };
456        let p = CodegenPrimitive::new(ty);
457        let actual: syn::Type = parse_quote!(#p);
458        let expected: syn::Type = parse_quote!(bool);
459        assert_eq!(actual, expected);
460    }
461
462    #[test]
463    fn test_codegen_primitive_datetime_default_rfc3339() {
464        let doc = Document::from_yaml(indoc::indoc! {"
465            openapi: 3.0.0
466            info:
467              title: Test
468              version: 1.0.0
469            paths: {}
470            components:
471              schemas:
472                Test:
473                  type: object
474                  required: [value]
475                  properties:
476                    value:
477                      type: string
478                      format: date-time
479        "})
480        .unwrap();
481        let arena = Arena::new();
482        let spec = Spec::from_doc(&arena, &doc).unwrap();
483        // Default config for `CodegenGraph` uses RFC 3339.
484        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
485        let primitives = graph.primitives().collect_vec();
486        let [ty] = &*primitives else {
487            panic!("expected datetime; got `{primitives:?}`");
488        };
489        let p = CodegenPrimitive::new(ty);
490        let actual: syn::Type = parse_quote!(#p);
491        let expected: syn::Type =
492            parse_quote!(::ploidy_util::chrono::DateTime<::ploidy_util::chrono::Utc>);
493        assert_eq!(actual, expected);
494    }
495
496    #[test]
497    fn test_codegen_primitive_datetime_unix_milliseconds() {
498        let doc = Document::from_yaml(indoc::indoc! {"
499            openapi: 3.0.0
500            info:
501              title: Test
502              version: 1.0.0
503            paths: {}
504            components:
505              schemas:
506                Test:
507                  type: object
508                  required: [value]
509                  properties:
510                    value:
511                      type: string
512                      format: date-time
513        "})
514        .unwrap();
515        let arena = Arena::new();
516        let spec = Spec::from_doc(&arena, &doc).unwrap();
517        let graph = CodegenGraph::with_config(
518            RawGraph::new(&arena, &spec).cook(),
519            &CodegenConfig {
520                date_time_format: DateTimeFormat::UnixMilliseconds,
521            },
522        );
523        let primitives = graph.primitives().collect_vec();
524        let [ty] = &*primitives else {
525            panic!("expected datetime; got `{primitives:?}`");
526        };
527        let p = CodegenPrimitive::new(ty);
528        let actual: syn::Type = parse_quote!(#p);
529        let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixMilliseconds);
530        assert_eq!(actual, expected);
531    }
532
533    #[test]
534    fn test_codegen_primitive_datetime_unix_seconds() {
535        let doc = Document::from_yaml(indoc::indoc! {"
536            openapi: 3.0.0
537            info:
538              title: Test
539              version: 1.0.0
540            paths: {}
541            components:
542              schemas:
543                Test:
544                  type: object
545                  required: [value]
546                  properties:
547                    value:
548                      type: string
549                      format: date-time
550        "})
551        .unwrap();
552        let arena = Arena::new();
553        let spec = Spec::from_doc(&arena, &doc).unwrap();
554        let graph = CodegenGraph::with_config(
555            RawGraph::new(&arena, &spec).cook(),
556            &CodegenConfig {
557                date_time_format: DateTimeFormat::UnixSeconds,
558            },
559        );
560        let primitives = graph.primitives().collect_vec();
561        let [ty] = &*primitives else {
562            panic!("expected datetime; got `{primitives:?}`");
563        };
564        let p = CodegenPrimitive::new(ty);
565        let actual: syn::Type = parse_quote!(#p);
566        let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixSeconds);
567        assert_eq!(actual, expected);
568    }
569
570    #[test]
571    fn test_codegen_primitive_datetime_unix_microseconds() {
572        let doc = Document::from_yaml(indoc::indoc! {"
573            openapi: 3.0.0
574            info:
575              title: Test
576              version: 1.0.0
577            paths: {}
578            components:
579              schemas:
580                Test:
581                  type: object
582                  required: [value]
583                  properties:
584                    value:
585                      type: string
586                      format: date-time
587        "})
588        .unwrap();
589        let arena = Arena::new();
590        let spec = Spec::from_doc(&arena, &doc).unwrap();
591        let graph = CodegenGraph::with_config(
592            RawGraph::new(&arena, &spec).cook(),
593            &CodegenConfig {
594                date_time_format: DateTimeFormat::UnixMicroseconds,
595            },
596        );
597        let primitives = graph.primitives().collect_vec();
598        let [ty] = &*primitives else {
599            panic!("expected datetime; got `{primitives:?}`");
600        };
601        let p = CodegenPrimitive::new(ty);
602        let actual: syn::Type = parse_quote!(#p);
603        let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixMicroseconds);
604        assert_eq!(actual, expected);
605    }
606
607    #[test]
608    fn test_codegen_primitive_datetime_unix_nanoseconds() {
609        let doc = Document::from_yaml(indoc::indoc! {"
610            openapi: 3.0.0
611            info:
612              title: Test
613              version: 1.0.0
614            paths: {}
615            components:
616              schemas:
617                Test:
618                  type: object
619                  required: [value]
620                  properties:
621                    value:
622                      type: string
623                      format: date-time
624        "})
625        .unwrap();
626        let arena = Arena::new();
627        let spec = Spec::from_doc(&arena, &doc).unwrap();
628        let graph = CodegenGraph::with_config(
629            RawGraph::new(&arena, &spec).cook(),
630            &CodegenConfig {
631                date_time_format: DateTimeFormat::UnixNanoseconds,
632            },
633        );
634        let primitives = graph.primitives().collect_vec();
635        let [ty] = &*primitives else {
636            panic!("expected datetime; got `{primitives:?}`");
637        };
638        let p = CodegenPrimitive::new(ty);
639        let actual: syn::Type = parse_quote!(#p);
640        let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixNanoseconds);
641        assert_eq!(actual, expected);
642    }
643
644    #[test]
645    fn test_codegen_primitive_date() {
646        let doc = Document::from_yaml(indoc::indoc! {"
647            openapi: 3.0.0
648            info:
649              title: Test
650              version: 1.0.0
651            paths: {}
652            components:
653              schemas:
654                Test:
655                  type: object
656                  required: [value]
657                  properties:
658                    value:
659                      type: string
660                      format: date
661        "})
662        .unwrap();
663        let arena = Arena::new();
664        let spec = Spec::from_doc(&arena, &doc).unwrap();
665        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
666        let primitives = graph.primitives().collect_vec();
667        let [ty] = &*primitives else {
668            panic!("expected date; got `{primitives:?}`");
669        };
670        let p = CodegenPrimitive::new(ty);
671        let actual: syn::Type = parse_quote!(#p);
672        let expected: syn::Type = parse_quote!(::ploidy_util::chrono::NaiveDate);
673        assert_eq!(actual, expected);
674    }
675
676    #[test]
677    fn test_codegen_primitive_url() {
678        let doc = Document::from_yaml(indoc::indoc! {"
679            openapi: 3.0.0
680            info:
681              title: Test
682              version: 1.0.0
683            paths: {}
684            components:
685              schemas:
686                Test:
687                  type: object
688                  required: [value]
689                  properties:
690                    value:
691                      type: string
692                      format: uri
693        "})
694        .unwrap();
695        let arena = Arena::new();
696        let spec = Spec::from_doc(&arena, &doc).unwrap();
697        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
698        let primitives = graph.primitives().collect_vec();
699        let [ty] = &*primitives else {
700            panic!("expected url; got `{primitives:?}`");
701        };
702        let p = CodegenPrimitive::new(ty);
703        let actual: syn::Type = parse_quote!(#p);
704        let expected: syn::Type = parse_quote!(::ploidy_util::url::Url);
705        assert_eq!(actual, expected);
706    }
707
708    #[test]
709    fn test_codegen_primitive_uuid() {
710        let doc = Document::from_yaml(indoc::indoc! {"
711            openapi: 3.0.0
712            info:
713              title: Test
714              version: 1.0.0
715            paths: {}
716            components:
717              schemas:
718                Test:
719                  type: object
720                  required: [value]
721                  properties:
722                    value:
723                      type: string
724                      format: uuid
725        "})
726        .unwrap();
727        let arena = Arena::new();
728        let spec = Spec::from_doc(&arena, &doc).unwrap();
729        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
730        let primitives = graph.primitives().collect_vec();
731        let [ty] = &*primitives else {
732            panic!("expected uuid; got `{primitives:?}`");
733        };
734        let p = CodegenPrimitive::new(ty);
735        let actual: syn::Type = parse_quote!(#p);
736        let expected: syn::Type = parse_quote!(::ploidy_util::uuid::Uuid);
737        assert_eq!(actual, expected);
738    }
739
740    #[test]
741    fn test_codegen_primitive_bytes() {
742        let doc = Document::from_yaml(indoc::indoc! {"
743            openapi: 3.0.0
744            info:
745              title: Test
746              version: 1.0.0
747            paths: {}
748            components:
749              schemas:
750                Test:
751                  type: object
752                  required: [value]
753                  properties:
754                    value:
755                      type: string
756                      format: byte
757        "})
758        .unwrap();
759        let arena = Arena::new();
760        let spec = Spec::from_doc(&arena, &doc).unwrap();
761        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
762        let primitives = graph.primitives().collect_vec();
763        let [ty] = &*primitives else {
764            panic!("expected bytes; got `{primitives:?}`");
765        };
766        let p = CodegenPrimitive::new(ty);
767        let actual: syn::Type = parse_quote!(#p);
768        let expected: syn::Type = parse_quote!(::ploidy_util::binary::Base64);
769        assert_eq!(actual, expected);
770    }
771
772    #[test]
773    fn test_codegen_primitive_binary() {
774        let doc = Document::from_yaml(indoc::indoc! {"
775            openapi: 3.0.0
776            info:
777              title: Test
778              version: 1.0.0
779            paths: {}
780            components:
781              schemas:
782                Test:
783                  type: object
784                  required: [value]
785                  properties:
786                    value:
787                      type: string
788                      format: binary
789        "})
790        .unwrap();
791        let arena = Arena::new();
792        let spec = Spec::from_doc(&arena, &doc).unwrap();
793        let graph = CodegenGraph::new(RawGraph::new(&arena, &spec).cook());
794        let primitives = graph.primitives().collect_vec();
795        let [ty] = &*primitives else {
796            panic!("expected binary; got `{primitives:?}`");
797        };
798        let p = CodegenPrimitive::new(ty);
799        let actual: syn::Type = parse_quote!(#p);
800        let expected: syn::Type = parse_quote!(::ploidy_util::serde_bytes::ByteBuf);
801        assert_eq!(actual, expected);
802    }
803}