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::I32 => quote! { i32 },
23 PrimitiveIrType::I64 => quote! { i64 },
24 PrimitiveIrType::F32 => quote! { f32 },
25 PrimitiveIrType::F64 => quote! { f64 },
26 PrimitiveIrType::Bool => quote! { bool },
27 PrimitiveIrType::DateTime => {
28 let format = self
29 .ty
30 .extensions()
31 .get::<DateTimeFormat>()
32 .as_deref()
33 .copied()
34 .unwrap_or_default();
35 match format {
36 DateTimeFormat::Rfc3339 => {
37 quote! { ::chrono::DateTime<::chrono::Utc> }
38 }
39 DateTimeFormat::UnixSeconds => {
40 quote! { ::ploidy_util::date_time::UnixSeconds }
41 }
42 DateTimeFormat::UnixMilliseconds => {
43 quote! { ::ploidy_util::date_time::UnixMilliseconds }
44 }
45 DateTimeFormat::UnixMicroseconds => {
46 quote! { ::ploidy_util::date_time::UnixMicroseconds }
47 }
48 DateTimeFormat::UnixNanoseconds => {
49 quote! { ::ploidy_util::date_time::UnixNanoseconds }
50 }
51 }
52 }
53 PrimitiveIrType::UnixTime => quote! { ::ploidy_util::date_time::UnixSeconds },
54 PrimitiveIrType::Date => quote! { ::chrono::NaiveDate },
55 PrimitiveIrType::Url => quote! { ::url::Url },
56 PrimitiveIrType::Uuid => quote! { ::uuid::Uuid },
57 PrimitiveIrType::Bytes => quote! { ::bytes::Bytes },
58 });
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 use itertools::Itertools;
67 use ploidy_core::{
68 ir::{IrGraph, IrSpec},
69 parse::Document,
70 };
71 use pretty_assertions::assert_eq;
72 use syn::parse_quote;
73
74 use crate::{CodegenConfig, CodegenGraph, DateTimeFormat};
75
76 #[test]
77 fn test_codegen_primitive_string() {
78 let doc = Document::from_yaml(indoc::indoc! {"
79 openapi: 3.0.0
80 info:
81 title: Test
82 version: 1.0.0
83 paths: {}
84 components:
85 schemas:
86 Test:
87 type: string
88 "})
89 .unwrap();
90 let spec = IrSpec::from_doc(&doc).unwrap();
91 let graph = CodegenGraph::new(IrGraph::new(&spec));
92 let primitives = graph.primitives().collect_vec();
93 let [ty] = &*primitives else {
94 panic!("expected string; got `{primitives:?}`");
95 };
96 let p = CodegenPrimitive::new(ty);
97 let actual: syn::Type = parse_quote!(#p);
98 let expected: syn::Type = parse_quote!(::std::string::String);
99 assert_eq!(actual, expected);
100 }
101
102 #[test]
103 fn test_codegen_primitive_i32() {
104 let doc = Document::from_yaml(indoc::indoc! {"
105 openapi: 3.0.0
106 info:
107 title: Test
108 version: 1.0.0
109 paths: {}
110 components:
111 schemas:
112 Test:
113 type: object
114 required: [value]
115 properties:
116 value:
117 type: integer
118 format: int32
119 "})
120 .unwrap();
121 let spec = IrSpec::from_doc(&doc).unwrap();
122 let graph = CodegenGraph::new(IrGraph::new(&spec));
123 let primitives = graph.primitives().collect_vec();
124 let [ty] = &*primitives else {
125 panic!("expected string; got `{primitives:?}`");
126 };
127 let p = CodegenPrimitive::new(ty);
128 let actual: syn::Type = parse_quote!(#p);
129 let expected: syn::Type = parse_quote!(i32);
130 assert_eq!(actual, expected);
131 }
132
133 #[test]
134 fn test_codegen_primitive_i64() {
135 let doc = Document::from_yaml(indoc::indoc! {"
136 openapi: 3.0.0
137 info:
138 title: Test
139 version: 1.0.0
140 paths: {}
141 components:
142 schemas:
143 Test:
144 type: object
145 required: [value]
146 properties:
147 value:
148 type: integer
149 format: int64
150 "})
151 .unwrap();
152 let spec = IrSpec::from_doc(&doc).unwrap();
153 let graph = CodegenGraph::new(IrGraph::new(&spec));
154 let primitives = graph.primitives().collect_vec();
155 let [ty] = &*primitives else {
156 panic!("expected i64; got `{primitives:?}`");
157 };
158 let p = CodegenPrimitive::new(ty);
159 let actual: syn::Type = parse_quote!(#p);
160 let expected: syn::Type = parse_quote!(i64);
161 assert_eq!(actual, expected);
162 }
163
164 #[test]
165 fn test_codegen_primitive_f32() {
166 let doc = Document::from_yaml(indoc::indoc! {"
167 openapi: 3.0.0
168 info:
169 title: Test
170 version: 1.0.0
171 paths: {}
172 components:
173 schemas:
174 Test:
175 type: object
176 required: [value]
177 properties:
178 value:
179 type: number
180 format: float
181 "})
182 .unwrap();
183 let spec = IrSpec::from_doc(&doc).unwrap();
184 let graph = CodegenGraph::new(IrGraph::new(&spec));
185 let primitives = graph.primitives().collect_vec();
186 let [ty] = &*primitives else {
187 panic!("expected f32; got `{primitives:?}`");
188 };
189 let p = CodegenPrimitive::new(ty);
190 let actual: syn::Type = parse_quote!(#p);
191 let expected: syn::Type = parse_quote!(f32);
192 assert_eq!(actual, expected);
193 }
194
195 #[test]
196 fn test_codegen_primitive_f64() {
197 let doc = Document::from_yaml(indoc::indoc! {"
198 openapi: 3.0.0
199 info:
200 title: Test
201 version: 1.0.0
202 paths: {}
203 components:
204 schemas:
205 Test:
206 type: object
207 required: [value]
208 properties:
209 value:
210 type: number
211 format: double
212 "})
213 .unwrap();
214 let spec = IrSpec::from_doc(&doc).unwrap();
215 let graph = CodegenGraph::new(IrGraph::new(&spec));
216 let primitives = graph.primitives().collect_vec();
217 let [ty] = &*primitives else {
218 panic!("expected f64; got `{primitives:?}`");
219 };
220 let p = CodegenPrimitive::new(ty);
221 let actual: syn::Type = parse_quote!(#p);
222 let expected: syn::Type = parse_quote!(f64);
223 assert_eq!(actual, expected);
224 }
225
226 #[test]
227 fn test_codegen_primitive_bool() {
228 let doc = Document::from_yaml(indoc::indoc! {"
229 openapi: 3.0.0
230 info:
231 title: Test
232 version: 1.0.0
233 paths: {}
234 components:
235 schemas:
236 Test:
237 type: object
238 required: [value]
239 properties:
240 value:
241 type: boolean
242 "})
243 .unwrap();
244 let spec = IrSpec::from_doc(&doc).unwrap();
245 let graph = CodegenGraph::new(IrGraph::new(&spec));
246 let primitives = graph.primitives().collect_vec();
247 let [ty] = &*primitives else {
248 panic!("expected bool; got `{primitives:?}`");
249 };
250 let p = CodegenPrimitive::new(ty);
251 let actual: syn::Type = parse_quote!(#p);
252 let expected: syn::Type = parse_quote!(bool);
253 assert_eq!(actual, expected);
254 }
255
256 #[test]
257 fn test_codegen_primitive_datetime_default_rfc3339() {
258 let doc = Document::from_yaml(indoc::indoc! {"
259 openapi: 3.0.0
260 info:
261 title: Test
262 version: 1.0.0
263 paths: {}
264 components:
265 schemas:
266 Test:
267 type: object
268 required: [value]
269 properties:
270 value:
271 type: string
272 format: date-time
273 "})
274 .unwrap();
275 let spec = IrSpec::from_doc(&doc).unwrap();
276 let graph = CodegenGraph::new(IrGraph::new(&spec));
278 let primitives = graph.primitives().collect_vec();
279 let [ty] = &*primitives else {
280 panic!("expected datetime; got `{primitives:?}`");
281 };
282 let p = CodegenPrimitive::new(ty);
283 let actual: syn::Type = parse_quote!(#p);
284 let expected: syn::Type = parse_quote!(::chrono::DateTime<::chrono::Utc>);
285 assert_eq!(actual, expected);
286 }
287
288 #[test]
289 fn test_codegen_primitive_datetime_unix_milliseconds() {
290 let doc = Document::from_yaml(indoc::indoc! {"
291 openapi: 3.0.0
292 info:
293 title: Test
294 version: 1.0.0
295 paths: {}
296 components:
297 schemas:
298 Test:
299 type: object
300 required: [value]
301 properties:
302 value:
303 type: string
304 format: date-time
305 "})
306 .unwrap();
307 let spec = IrSpec::from_doc(&doc).unwrap();
308 let graph = CodegenGraph::with_config(
309 IrGraph::new(&spec),
310 &CodegenConfig {
311 date_time_format: DateTimeFormat::UnixMilliseconds,
312 },
313 );
314 let primitives = graph.primitives().collect_vec();
315 let [ty] = &*primitives else {
316 panic!("expected datetime; got `{primitives:?}`");
317 };
318 let p = CodegenPrimitive::new(ty);
319 let actual: syn::Type = parse_quote!(#p);
320 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixMilliseconds);
321 assert_eq!(actual, expected);
322 }
323
324 #[test]
325 fn test_codegen_primitive_datetime_unix_seconds() {
326 let doc = Document::from_yaml(indoc::indoc! {"
327 openapi: 3.0.0
328 info:
329 title: Test
330 version: 1.0.0
331 paths: {}
332 components:
333 schemas:
334 Test:
335 type: object
336 required: [value]
337 properties:
338 value:
339 type: string
340 format: date-time
341 "})
342 .unwrap();
343 let spec = IrSpec::from_doc(&doc).unwrap();
344 let graph = CodegenGraph::with_config(
345 IrGraph::new(&spec),
346 &CodegenConfig {
347 date_time_format: DateTimeFormat::UnixSeconds,
348 },
349 );
350 let primitives = graph.primitives().collect_vec();
351 let [ty] = &*primitives else {
352 panic!("expected datetime; got `{primitives:?}`");
353 };
354 let p = CodegenPrimitive::new(ty);
355 let actual: syn::Type = parse_quote!(#p);
356 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixSeconds);
357 assert_eq!(actual, expected);
358 }
359
360 #[test]
361 fn test_codegen_primitive_datetime_unix_microseconds() {
362 let doc = Document::from_yaml(indoc::indoc! {"
363 openapi: 3.0.0
364 info:
365 title: Test
366 version: 1.0.0
367 paths: {}
368 components:
369 schemas:
370 Test:
371 type: object
372 required: [value]
373 properties:
374 value:
375 type: string
376 format: date-time
377 "})
378 .unwrap();
379 let spec = IrSpec::from_doc(&doc).unwrap();
380 let graph = CodegenGraph::with_config(
381 IrGraph::new(&spec),
382 &CodegenConfig {
383 date_time_format: DateTimeFormat::UnixMicroseconds,
384 },
385 );
386 let primitives = graph.primitives().collect_vec();
387 let [ty] = &*primitives else {
388 panic!("expected datetime; got `{primitives:?}`");
389 };
390 let p = CodegenPrimitive::new(ty);
391 let actual: syn::Type = parse_quote!(#p);
392 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixMicroseconds);
393 assert_eq!(actual, expected);
394 }
395
396 #[test]
397 fn test_codegen_primitive_datetime_unix_nanoseconds() {
398 let doc = Document::from_yaml(indoc::indoc! {"
399 openapi: 3.0.0
400 info:
401 title: Test
402 version: 1.0.0
403 paths: {}
404 components:
405 schemas:
406 Test:
407 type: object
408 required: [value]
409 properties:
410 value:
411 type: string
412 format: date-time
413 "})
414 .unwrap();
415 let spec = IrSpec::from_doc(&doc).unwrap();
416 let graph = CodegenGraph::with_config(
417 IrGraph::new(&spec),
418 &CodegenConfig {
419 date_time_format: DateTimeFormat::UnixNanoseconds,
420 },
421 );
422 let primitives = graph.primitives().collect_vec();
423 let [ty] = &*primitives else {
424 panic!("expected datetime; got `{primitives:?}`");
425 };
426 let p = CodegenPrimitive::new(ty);
427 let actual: syn::Type = parse_quote!(#p);
428 let expected: syn::Type = parse_quote!(::ploidy_util::date_time::UnixNanoseconds);
429 assert_eq!(actual, expected);
430 }
431
432 #[test]
433 fn test_codegen_primitive_date() {
434 let doc = Document::from_yaml(indoc::indoc! {"
435 openapi: 3.0.0
436 info:
437 title: Test
438 version: 1.0.0
439 paths: {}
440 components:
441 schemas:
442 Test:
443 type: object
444 required: [value]
445 properties:
446 value:
447 type: string
448 format: date
449 "})
450 .unwrap();
451 let spec = IrSpec::from_doc(&doc).unwrap();
452 let graph = CodegenGraph::new(IrGraph::new(&spec));
453 let primitives = graph.primitives().collect_vec();
454 let [ty] = &*primitives else {
455 panic!("expected date; got `{primitives:?}`");
456 };
457 let p = CodegenPrimitive::new(ty);
458 let actual: syn::Type = parse_quote!(#p);
459 let expected: syn::Type = parse_quote!(::chrono::NaiveDate);
460 assert_eq!(actual, expected);
461 }
462
463 #[test]
464 fn test_codegen_primitive_url() {
465 let doc = Document::from_yaml(indoc::indoc! {"
466 openapi: 3.0.0
467 info:
468 title: Test
469 version: 1.0.0
470 paths: {}
471 components:
472 schemas:
473 Test:
474 type: object
475 required: [value]
476 properties:
477 value:
478 type: string
479 format: uri
480 "})
481 .unwrap();
482 let spec = IrSpec::from_doc(&doc).unwrap();
483 let graph = CodegenGraph::new(IrGraph::new(&spec));
484 let primitives = graph.primitives().collect_vec();
485 let [ty] = &*primitives else {
486 panic!("expected url; got `{primitives:?}`");
487 };
488 let p = CodegenPrimitive::new(ty);
489 let actual: syn::Type = parse_quote!(#p);
490 let expected: syn::Type = parse_quote!(::url::Url);
491 assert_eq!(actual, expected);
492 }
493
494 #[test]
495 fn test_codegen_primitive_uuid() {
496 let doc = Document::from_yaml(indoc::indoc! {"
497 openapi: 3.0.0
498 info:
499 title: Test
500 version: 1.0.0
501 paths: {}
502 components:
503 schemas:
504 Test:
505 type: object
506 required: [value]
507 properties:
508 value:
509 type: string
510 format: uuid
511 "})
512 .unwrap();
513 let spec = IrSpec::from_doc(&doc).unwrap();
514 let graph = CodegenGraph::new(IrGraph::new(&spec));
515 let primitives = graph.primitives().collect_vec();
516 let [ty] = &*primitives else {
517 panic!("expected uuid; got `{primitives:?}`");
518 };
519 let p = CodegenPrimitive::new(ty);
520 let actual: syn::Type = parse_quote!(#p);
521 let expected: syn::Type = parse_quote!(::uuid::Uuid);
522 assert_eq!(actual, expected);
523 }
524
525 #[test]
526 fn test_codegen_primitive_bytes() {
527 let doc = Document::from_yaml(indoc::indoc! {"
528 openapi: 3.0.0
529 info:
530 title: Test
531 version: 1.0.0
532 paths: {}
533 components:
534 schemas:
535 Test:
536 type: object
537 required: [value]
538 properties:
539 value:
540 type: string
541 format: byte
542 "})
543 .unwrap();
544 let spec = IrSpec::from_doc(&doc).unwrap();
545 let graph = CodegenGraph::new(IrGraph::new(&spec));
546 let primitives = graph.primitives().collect_vec();
547 let [ty] = &*primitives else {
548 panic!("expected bytes; got `{primitives:?}`");
549 };
550 let p = CodegenPrimitive::new(ty);
551 let actual: syn::Type = parse_quote!(#p);
552 let expected: syn::Type = parse_quote!(::bytes::Bytes);
553 assert_eq!(actual, expected);
554 }
555}