ploidy_codegen_rust/
primitive.rs1use ploidy_core::ir::{ExtendableView, IrPrimitiveView, PrimitiveIrType};
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! { ::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 PrimitiveIrType::UnixTime => quote! { ::ploidy_util::date_time::UnixSeconds },
60 PrimitiveIrType::Date => quote! { ::ploidy_util::chrono::NaiveDate },
61 PrimitiveIrType::Url => quote! { ::ploidy_util::url::Url },
62 PrimitiveIrType::Uuid => quote! { ::ploidy_util::uuid::Uuid },
63 PrimitiveIrType::Bytes => quote! { ::ploidy_util::binary::Base64 },
64 PrimitiveIrType::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 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 =
478 parse_quote!(::ploidy_util::chrono::DateTime<::ploidy_util::chrono::Utc>);
479 assert_eq!(actual, expected);
480 }
481
482 #[test]
483 fn test_codegen_primitive_datetime_unix_milliseconds() {
484 let doc = Document::from_yaml(indoc::indoc! {"
485 openapi: 3.0.0
486 info:
487 title: Test
488 version: 1.0.0
489 paths: {}
490 components:
491 schemas:
492 Test:
493 type: object
494 required: [value]
495 properties:
496 value:
497 type: string
498 format: date-time
499 "})
500 .unwrap();
501 let spec = IrSpec::from_doc(&doc).unwrap();
502 let graph = CodegenGraph::with_config(
503 IrGraph::new(&spec),
504 &CodegenConfig {
505 date_time_format: DateTimeFormat::UnixMilliseconds,
506 },
507 );
508 let primitives = graph.primitives().collect_vec();
509 let [ty] = &*primitives else {
510 panic!("expected datetime; got `{primitives:?}`");
511 };
512 let p = CodegenPrimitive::new(ty);
513 let actual: syn::Type = parse_quote!(#p);
514 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixMilliseconds);
515 assert_eq!(actual, expected);
516 }
517
518 #[test]
519 fn test_codegen_primitive_datetime_unix_seconds() {
520 let doc = Document::from_yaml(indoc::indoc! {"
521 openapi: 3.0.0
522 info:
523 title: Test
524 version: 1.0.0
525 paths: {}
526 components:
527 schemas:
528 Test:
529 type: object
530 required: [value]
531 properties:
532 value:
533 type: string
534 format: date-time
535 "})
536 .unwrap();
537 let spec = IrSpec::from_doc(&doc).unwrap();
538 let graph = CodegenGraph::with_config(
539 IrGraph::new(&spec),
540 &CodegenConfig {
541 date_time_format: DateTimeFormat::UnixSeconds,
542 },
543 );
544 let primitives = graph.primitives().collect_vec();
545 let [ty] = &*primitives else {
546 panic!("expected datetime; got `{primitives:?}`");
547 };
548 let p = CodegenPrimitive::new(ty);
549 let actual: syn::Type = parse_quote!(#p);
550 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixSeconds);
551 assert_eq!(actual, expected);
552 }
553
554 #[test]
555 fn test_codegen_primitive_datetime_unix_microseconds() {
556 let doc = Document::from_yaml(indoc::indoc! {"
557 openapi: 3.0.0
558 info:
559 title: Test
560 version: 1.0.0
561 paths: {}
562 components:
563 schemas:
564 Test:
565 type: object
566 required: [value]
567 properties:
568 value:
569 type: string
570 format: date-time
571 "})
572 .unwrap();
573 let spec = IrSpec::from_doc(&doc).unwrap();
574 let graph = CodegenGraph::with_config(
575 IrGraph::new(&spec),
576 &CodegenConfig {
577 date_time_format: DateTimeFormat::UnixMicroseconds,
578 },
579 );
580 let primitives = graph.primitives().collect_vec();
581 let [ty] = &*primitives else {
582 panic!("expected datetime; got `{primitives:?}`");
583 };
584 let p = CodegenPrimitive::new(ty);
585 let actual: syn::Type = parse_quote!(#p);
586 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixMicroseconds);
587 assert_eq!(actual, expected);
588 }
589
590 #[test]
591 fn test_codegen_primitive_datetime_unix_nanoseconds() {
592 let doc = Document::from_yaml(indoc::indoc! {"
593 openapi: 3.0.0
594 info:
595 title: Test
596 version: 1.0.0
597 paths: {}
598 components:
599 schemas:
600 Test:
601 type: object
602 required: [value]
603 properties:
604 value:
605 type: string
606 format: date-time
607 "})
608 .unwrap();
609 let spec = IrSpec::from_doc(&doc).unwrap();
610 let graph = CodegenGraph::with_config(
611 IrGraph::new(&spec),
612 &CodegenConfig {
613 date_time_format: DateTimeFormat::UnixNanoseconds,
614 },
615 );
616 let primitives = graph.primitives().collect_vec();
617 let [ty] = &*primitives else {
618 panic!("expected datetime; got `{primitives:?}`");
619 };
620 let p = CodegenPrimitive::new(ty);
621 let actual: syn::Type = parse_quote!(#p);
622 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixNanoseconds);
623 assert_eq!(actual, expected);
624 }
625
626 #[test]
627 fn test_codegen_primitive_date() {
628 let doc = Document::from_yaml(indoc::indoc! {"
629 openapi: 3.0.0
630 info:
631 title: Test
632 version: 1.0.0
633 paths: {}
634 components:
635 schemas:
636 Test:
637 type: object
638 required: [value]
639 properties:
640 value:
641 type: string
642 format: date
643 "})
644 .unwrap();
645 let spec = IrSpec::from_doc(&doc).unwrap();
646 let graph = CodegenGraph::new(IrGraph::new(&spec));
647 let primitives = graph.primitives().collect_vec();
648 let [ty] = &*primitives else {
649 panic!("expected date; got `{primitives:?}`");
650 };
651 let p = CodegenPrimitive::new(ty);
652 let actual: syn::Type = parse_quote!(#p);
653 let expected: syn::Type = parse_quote!(::ploidy_util::chrono::NaiveDate);
654 assert_eq!(actual, expected);
655 }
656
657 #[test]
658 fn test_codegen_primitive_url() {
659 let doc = Document::from_yaml(indoc::indoc! {"
660 openapi: 3.0.0
661 info:
662 title: Test
663 version: 1.0.0
664 paths: {}
665 components:
666 schemas:
667 Test:
668 type: object
669 required: [value]
670 properties:
671 value:
672 type: string
673 format: uri
674 "})
675 .unwrap();
676 let spec = IrSpec::from_doc(&doc).unwrap();
677 let graph = CodegenGraph::new(IrGraph::new(&spec));
678 let primitives = graph.primitives().collect_vec();
679 let [ty] = &*primitives else {
680 panic!("expected url; got `{primitives:?}`");
681 };
682 let p = CodegenPrimitive::new(ty);
683 let actual: syn::Type = parse_quote!(#p);
684 let expected: syn::Type = parse_quote!(::ploidy_util::url::Url);
685 assert_eq!(actual, expected);
686 }
687
688 #[test]
689 fn test_codegen_primitive_uuid() {
690 let doc = Document::from_yaml(indoc::indoc! {"
691 openapi: 3.0.0
692 info:
693 title: Test
694 version: 1.0.0
695 paths: {}
696 components:
697 schemas:
698 Test:
699 type: object
700 required: [value]
701 properties:
702 value:
703 type: string
704 format: uuid
705 "})
706 .unwrap();
707 let spec = IrSpec::from_doc(&doc).unwrap();
708 let graph = CodegenGraph::new(IrGraph::new(&spec));
709 let primitives = graph.primitives().collect_vec();
710 let [ty] = &*primitives else {
711 panic!("expected uuid; got `{primitives:?}`");
712 };
713 let p = CodegenPrimitive::new(ty);
714 let actual: syn::Type = parse_quote!(#p);
715 let expected: syn::Type = parse_quote!(::ploidy_util::uuid::Uuid);
716 assert_eq!(actual, expected);
717 }
718
719 #[test]
720 fn test_codegen_primitive_bytes() {
721 let doc = Document::from_yaml(indoc::indoc! {"
722 openapi: 3.0.0
723 info:
724 title: Test
725 version: 1.0.0
726 paths: {}
727 components:
728 schemas:
729 Test:
730 type: object
731 required: [value]
732 properties:
733 value:
734 type: string
735 format: byte
736 "})
737 .unwrap();
738 let spec = IrSpec::from_doc(&doc).unwrap();
739 let graph = CodegenGraph::new(IrGraph::new(&spec));
740 let primitives = graph.primitives().collect_vec();
741 let [ty] = &*primitives else {
742 panic!("expected bytes; got `{primitives:?}`");
743 };
744 let p = CodegenPrimitive::new(ty);
745 let actual: syn::Type = parse_quote!(#p);
746 let expected: syn::Type = parse_quote!(::ploidy_util::binary::Base64);
747 assert_eq!(actual, expected);
748 }
749
750 #[test]
751 fn test_codegen_primitive_binary() {
752 let doc = Document::from_yaml(indoc::indoc! {"
753 openapi: 3.0.0
754 info:
755 title: Test
756 version: 1.0.0
757 paths: {}
758 components:
759 schemas:
760 Test:
761 type: object
762 required: [value]
763 properties:
764 value:
765 type: string
766 format: binary
767 "})
768 .unwrap();
769 let spec = IrSpec::from_doc(&doc).unwrap();
770 let graph = CodegenGraph::new(IrGraph::new(&spec));
771 let primitives = graph.primitives().collect_vec();
772 let [ty] = &*primitives else {
773 panic!("expected binary; got `{primitives:?}`");
774 };
775 let p = CodegenPrimitive::new(ty);
776 let actual: syn::Type = parse_quote!(#p);
777 let expected: syn::Type = parse_quote!(::ploidy_util::serde_bytes::ByteBuf);
778 assert_eq!(actual, expected);
779 }
780}