Skip to main content

ploidy_codegen_rust/
primitive.rs

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