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