1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::spanned::Spanned;
4use syn::{parse::Parse, Error, Ident, LitStr, Path};
5
6use crate::{Diagnostics, ToTokensDiagnostics};
7
8#[cfg_attr(feature = "debug", derive(Debug))]
10#[allow(dead_code)]
11pub enum SchemaTypeInner {
12 Object,
14 String,
16 Integer,
18 Number,
20 Boolean,
22 Array,
24 Null,
26}
27
28impl ToTokens for SchemaTypeInner {
29 fn to_tokens(&self, tokens: &mut TokenStream) {
30 let ty = match self {
31 Self::Object => quote! { utoipa::openapi::schema::Type::Object },
32 Self::String => quote! { utoipa::openapi::schema::Type::String },
33 Self::Integer => quote! { utoipa::openapi::schema::Type::Integer },
34 Self::Number => quote! { utoipa::openapi::schema::Type::Number },
35 Self::Boolean => quote! { utoipa::openapi::schema::Type::Boolean },
36 Self::Array => quote! { utoipa::openapi::schema::Type::Array },
37 Self::Null => quote! { utoipa::openapi::schema::Type::Null },
38 };
39 tokens.extend(ty)
40 }
41}
42
43#[cfg_attr(feature = "debug", derive(Debug))]
45pub struct SchemaType<'a> {
46 pub path: std::borrow::Cow<'a, syn::Path>,
47 pub nullable: bool,
48}
49
50impl SchemaType<'_> {
51 fn last_segment_to_string(&self) -> String {
52 self.path
53 .segments
54 .last()
55 .expect("Expected at least one segment is_integer")
56 .ident
57 .to_string()
58 }
59
60 pub fn is_value(&self) -> bool {
61 matches!(&*self.last_segment_to_string(), "Value")
62 }
63
64 pub fn is_primitive(&self) -> bool {
66 let SchemaType { path, .. } = self;
67 let last_segment = match path.segments.last() {
68 Some(segment) => segment,
69 None => return false,
70 };
71 let name = &*last_segment.ident.to_string();
72
73 #[cfg(not(any(
74 feature = "chrono",
75 feature = "decimal",
76 feature = "decimal_float",
77 feature = "rocket_extras",
78 feature = "uuid",
79 feature = "ulid",
80 feature = "url",
81 feature = "time",
82 )))]
83 {
84 is_primitive(name)
85 }
86
87 #[cfg(any(
88 feature = "chrono",
89 feature = "decimal",
90 feature = "decimal_float",
91 feature = "rocket_extras",
92 feature = "uuid",
93 feature = "ulid",
94 feature = "url",
95 feature = "time",
96 ))]
97 {
98 let mut primitive = is_primitive(name);
99
100 #[cfg(feature = "chrono")]
101 if !primitive {
102 primitive = is_primitive_chrono(name);
103 }
104
105 #[cfg(any(feature = "decimal", feature = "decimal_float"))]
106 if !primitive {
107 primitive = is_primitive_rust_decimal(name);
108 }
109
110 #[cfg(feature = "rocket_extras")]
111 if !primitive {
112 primitive = matches!(name, "PathBuf");
113 }
114
115 #[cfg(feature = "uuid")]
116 if !primitive {
117 primitive = matches!(name, "Uuid");
118 }
119
120 #[cfg(feature = "ulid")]
121 if !primitive {
122 primitive = matches!(name, "Ulid");
123 }
124
125 #[cfg(feature = "url")]
126 if !primitive {
127 primitive = matches!(name, "Url");
128 }
129
130 #[cfg(feature = "time")]
131 if !primitive {
132 primitive = matches!(
133 name,
134 "Date" | "PrimitiveDateTime" | "OffsetDateTime" | "Duration"
135 );
136 }
137
138 #[cfg(feature = "jiff_0_2")]
139 if !primitive {
140 primitive = matches!(name, "Zoned" | "Date");
141 }
142
143 primitive
144 }
145 }
146
147 pub fn is_integer(&self) -> bool {
148 matches!(
149 &*self.last_segment_to_string(),
150 "i8" | "i16"
151 | "i32"
152 | "i64"
153 | "i128"
154 | "isize"
155 | "u8"
156 | "u16"
157 | "u32"
158 | "u64"
159 | "u128"
160 | "usize"
161 )
162 }
163
164 pub fn is_unsigned_integer(&self) -> bool {
165 matches!(
166 &*self.last_segment_to_string(),
167 "u8" | "u16" | "u32" | "u64" | "u128" | "usize"
168 )
169 }
170
171 pub fn is_number(&self) -> bool {
172 match &*self.last_segment_to_string() {
173 "f32" | "f64" => true,
174 _ if self.is_integer() => true,
175 _ => false,
176 }
177 }
178
179 pub fn is_string(&self) -> bool {
180 matches!(&*self.last_segment_to_string(), "str" | "String")
181 }
182
183 pub fn is_byte(&self) -> bool {
184 matches!(&*self.last_segment_to_string(), "u8")
185 }
186}
187
188#[inline]
189fn is_primitive(name: &str) -> bool {
190 matches!(
191 name,
192 "String"
193 | "str"
194 | "char"
195 | "bool"
196 | "usize"
197 | "u8"
198 | "u16"
199 | "u32"
200 | "u64"
201 | "u128"
202 | "isize"
203 | "i8"
204 | "i16"
205 | "i32"
206 | "i64"
207 | "i128"
208 | "f32"
209 | "f64"
210 )
211}
212
213#[inline]
214#[cfg(feature = "chrono")]
215fn is_primitive_chrono(name: &str) -> bool {
216 matches!(
217 name,
218 "DateTime" | "Date" | "NaiveDate" | "NaiveTime" | "Duration" | "NaiveDateTime"
219 )
220}
221
222#[inline]
223#[cfg(any(feature = "decimal", feature = "decimal_float"))]
224fn is_primitive_rust_decimal(name: &str) -> bool {
225 matches!(name, "Decimal")
226}
227
228impl ToTokensDiagnostics for SchemaType<'_> {
229 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) -> Result<(), Diagnostics> {
230 let last_segment = self.path.segments.last().ok_or_else(|| {
231 Diagnostics::with_span(
232 self.path.span(),
233 "schema type should have at least one segment in the path",
234 )
235 })?;
236 let name = &*last_segment.ident.to_string();
237
238 fn schema_type_tokens(
239 tokens: &mut TokenStream,
240 schema_type: SchemaTypeInner,
241 nullable: bool,
242 ) {
243 if nullable {
244 tokens.extend(quote! {
245 {
246 use std::iter::FromIterator;
247 utoipa::openapi::schema::SchemaType::from_iter([
248 #schema_type,
249 utoipa::openapi::schema::Type::Null
250 ])
251 }
252 })
253 } else {
254 tokens.extend(quote! { utoipa::openapi::schema::SchemaType::new(#schema_type)});
255 }
256 }
257
258 match name {
259 "String" | "str" | "char" => {
260 schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable)
261 }
262
263 "bool" => schema_type_tokens(tokens, SchemaTypeInner::Boolean, self.nullable),
264
265 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
266 | "u128" | "usize" => {
267 schema_type_tokens(tokens, SchemaTypeInner::Integer, self.nullable)
268 }
269 "f32" | "f64" => schema_type_tokens(tokens, SchemaTypeInner::Number, self.nullable),
270
271 #[cfg(feature = "chrono")]
272 "DateTime" | "NaiveDateTime" | "NaiveDate" | "NaiveTime" => {
273 schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable)
274 }
275
276 #[cfg(any(feature = "chrono", feature = "time", feature = "jiff_0_2"))]
277 "Date" | "Duration" => {
278 schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable)
279 }
280
281 #[cfg(feature = "decimal")]
282 "Decimal" => schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable),
283
284 #[cfg(feature = "decimal_float")]
285 "Decimal" => schema_type_tokens(tokens, SchemaTypeInner::Number, self.nullable),
286
287 #[cfg(feature = "rocket_extras")]
288 "PathBuf" => schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable),
289
290 #[cfg(feature = "uuid")]
291 "Uuid" => schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable),
292
293 #[cfg(feature = "ulid")]
294 "Ulid" => schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable),
295
296 #[cfg(feature = "url")]
297 "Url" => schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable),
298
299 #[cfg(feature = "time")]
300 "PrimitiveDateTime" | "OffsetDateTime" => {
301 schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable)
302 }
303 #[cfg(feature = "jiff_0_2")]
304 "Zoned" => schema_type_tokens(tokens, SchemaTypeInner::String, self.nullable),
305 _ => schema_type_tokens(tokens, SchemaTypeInner::Object, self.nullable),
306 };
307
308 Ok(())
309 }
310}
311
312#[derive(Clone)]
314#[cfg_attr(feature = "debug", derive(Debug))]
315pub enum KnownFormat {
316 #[cfg(feature = "non_strict_integers")]
317 Int8,
318 #[cfg(feature = "non_strict_integers")]
319 Int16,
320 Int32,
321 Int64,
322 #[cfg(feature = "non_strict_integers")]
323 UInt8,
324 #[cfg(feature = "non_strict_integers")]
325 UInt16,
326 #[cfg(feature = "non_strict_integers")]
327 UInt32,
328 #[cfg(feature = "non_strict_integers")]
329 UInt64,
330 Float,
331 Double,
332 Byte,
333 Binary,
334 Date,
335 DateTime,
336 Duration,
337 Password,
338 #[cfg(feature = "uuid")]
339 Uuid,
340 #[cfg(feature = "ulid")]
341 Ulid,
342 #[cfg(feature = "url")]
343 Uri,
344 #[cfg(feature = "url")]
345 UriReference,
346 #[cfg(feature = "url")]
347 Iri,
348 #[cfg(feature = "url")]
349 IriReference,
350 Email,
351 IdnEmail,
352 Hostname,
353 IdnHostname,
354 Ipv4,
355 Ipv6,
356 UriTemplate,
357 JsonPointer,
358 RelativeJsonPointer,
359 Regex,
360 Custom(String),
362 #[allow(unused)]
365 Unknown,
366}
367
368impl KnownFormat {
369 pub fn from_path(path: &syn::Path) -> Result<Self, Diagnostics> {
370 let last_segment = path.segments.last().ok_or_else(|| {
371 Diagnostics::with_span(
372 path.span(),
373 "type should have at least one segment in the path",
374 )
375 })?;
376 let name = &*last_segment.ident.to_string();
377
378 let variant = match name {
379 #[cfg(feature = "non_strict_integers")]
380 "i8" => Self::Int8,
381 #[cfg(feature = "non_strict_integers")]
382 "u8" => Self::UInt8,
383 #[cfg(feature = "non_strict_integers")]
384 "i16" => Self::Int16,
385 #[cfg(feature = "non_strict_integers")]
386 "u16" => Self::UInt16,
387 #[cfg(feature = "non_strict_integers")]
388 "u32" => Self::UInt32,
389 #[cfg(feature = "non_strict_integers")]
390 "u64" => Self::UInt64,
391
392 #[cfg(not(feature = "non_strict_integers"))]
393 "i8" | "i16" | "u8" | "u16" | "u32" => Self::Int32,
394
395 #[cfg(not(feature = "non_strict_integers"))]
396 "u64" => Self::Int64,
397
398 "i32" => Self::Int32,
399 "i64" => Self::Int64,
400 "f32" => Self::Float,
401 "f64" => Self::Double,
402
403 #[cfg(feature = "chrono")]
404 "NaiveDate" => Self::Date,
405
406 #[cfg(feature = "chrono")]
407 "DateTime" | "NaiveDateTime" => Self::DateTime,
408
409 #[cfg(any(feature = "chrono", feature = "time", feature = "jiff_0_2"))]
410 "Date" => Self::Date,
411
412 #[cfg(feature = "decimal_float")]
413 "Decimal" => Self::Double,
414
415 #[cfg(feature = "uuid")]
416 "Uuid" => Self::Uuid,
417
418 #[cfg(feature = "ulid")]
419 "Ulid" => Self::Ulid,
420
421 #[cfg(feature = "url")]
422 "Url" => Self::Uri,
423
424 #[cfg(feature = "time")]
425 "PrimitiveDateTime" | "OffsetDateTime" => Self::DateTime,
426
427 #[cfg(feature = "jiff_0_2")]
428 "Zoned" => Self::DateTime,
429 _ => Self::Unknown,
430 };
431
432 Ok(variant)
433 }
434
435 pub fn is_known_format(&self) -> bool {
436 !matches!(self, Self::Unknown)
437 }
438
439 fn get_allowed_formats() -> String {
440 let default_formats = [
441 "Int32",
442 "Int64",
443 "Float",
444 "Double",
445 "Byte",
446 "Binary",
447 "Date",
448 "DateTime",
449 "Duration",
450 "Password",
451 #[cfg(feature = "uuid")]
452 "Uuid",
453 #[cfg(feature = "ulid")]
454 "Ulid",
455 #[cfg(feature = "url")]
456 "Uri",
457 #[cfg(feature = "url")]
458 "UriReference",
459 #[cfg(feature = "url")]
460 "Iri",
461 #[cfg(feature = "url")]
462 "IriReference",
463 "Email",
464 "IdnEmail",
465 "Hostname",
466 "IdnHostname",
467 "Ipv4",
468 "Ipv6",
469 "UriTemplate",
470 "JsonPointer",
471 "RelativeJsonPointer",
472 "Regex",
473 ];
474 #[cfg(feature = "non_strict_integers")]
475 let non_strict_integer_formats = [
476 "Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64",
477 ];
478
479 #[cfg(feature = "non_strict_integers")]
480 let formats = {
481 let mut formats = default_formats
482 .into_iter()
483 .chain(non_strict_integer_formats)
484 .collect::<Vec<_>>();
485 formats.sort_unstable();
486 formats.join(", ")
487 };
488 #[cfg(not(feature = "non_strict_integers"))]
489 let formats = {
490 let formats = default_formats.into_iter().collect::<Vec<_>>();
491 formats.join(", ")
492 };
493
494 formats
495 }
496}
497
498impl Parse for KnownFormat {
499 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
500 let formats = KnownFormat::get_allowed_formats();
501
502 let lookahead = input.lookahead1();
503 if lookahead.peek(Ident) {
504 let format = input.parse::<Ident>()?;
505 let name = &*format.to_string();
506
507 match name {
508 #[cfg(feature = "non_strict_integers")]
509 "Int8" => Ok(Self::Int8),
510 #[cfg(feature = "non_strict_integers")]
511 "Int16" => Ok(Self::Int16),
512 "Int32" => Ok(Self::Int32),
513 "Int64" => Ok(Self::Int64),
514 #[cfg(feature = "non_strict_integers")]
515 "UInt8" => Ok(Self::UInt8),
516 #[cfg(feature = "non_strict_integers")]
517 "UInt16" => Ok(Self::UInt16),
518 #[cfg(feature = "non_strict_integers")]
519 "UInt32" => Ok(Self::UInt32),
520 #[cfg(feature = "non_strict_integers")]
521 "UInt64" => Ok(Self::UInt64),
522 "Float" => Ok(Self::Float),
523 "Double" => Ok(Self::Double),
524 "Byte" => Ok(Self::Byte),
525 "Binary" => Ok(Self::Binary),
526 "Date" => Ok(Self::Date),
527 "DateTime" => Ok(Self::DateTime),
528 "Duration" => Ok(Self::Duration),
529 "Password" => Ok(Self::Password),
530 #[cfg(feature = "uuid")]
531 "Uuid" => Ok(Self::Uuid),
532 #[cfg(feature = "ulid")]
533 "Ulid" => Ok(Self::Ulid),
534 #[cfg(feature = "url")]
535 "Uri" => Ok(Self::Uri),
536 #[cfg(feature = "url")]
537 "UriReference" => Ok(Self::UriReference),
538 #[cfg(feature = "url")]
539 "Iri" => Ok(Self::Iri),
540 #[cfg(feature = "url")]
541 "IriReference" => Ok(Self::IriReference),
542 "Email" => Ok(Self::Email),
543 "IdnEmail" => Ok(Self::IdnEmail),
544 "Hostname" => Ok(Self::Hostname),
545 "IdnHostname" => Ok(Self::IdnHostname),
546 "Ipv4" => Ok(Self::Ipv4),
547 "Ipv6" => Ok(Self::Ipv6),
548 "UriTemplate" => Ok(Self::UriTemplate),
549 "JsonPointer" => Ok(Self::JsonPointer),
550 "RelativeJsonPointer" => Ok(Self::RelativeJsonPointer),
551 "Regex" => Ok(Self::Regex),
552 _ => Err(Error::new(
553 format.span(),
554 format!("unexpected format: {name}, expected one of: {formats}"),
555 )),
556 }
557 } else if lookahead.peek(LitStr) {
558 let value = input.parse::<LitStr>()?.value();
559 Ok(Self::Custom(value))
560 } else {
561 Err(lookahead.error())
562 }
563 }
564}
565
566impl ToTokens for KnownFormat {
567 fn to_tokens(&self, tokens: &mut TokenStream) {
568 match self {
569 #[cfg(feature = "non_strict_integers")]
570 Self::Int8 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::Int8)}),
571 #[cfg(feature = "non_strict_integers")]
572 Self::Int16 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::Int16)}),
573 Self::Int32 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
574 utoipa::openapi::schema::KnownFormat::Int32
575 ))),
576 Self::Int64 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
577 utoipa::openapi::schema::KnownFormat::Int64
578 ))),
579 #[cfg(feature = "non_strict_integers")]
580 Self::UInt8 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::UInt8)}),
581 #[cfg(feature = "non_strict_integers")]
582 Self::UInt16 => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(utoipa::openapi::schema::KnownFormat::UInt16)}),
583 #[cfg(feature = "non_strict_integers")]
584 Self::UInt32 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
585 utoipa::openapi::schema::KnownFormat::UInt32
586 ))),
587 #[cfg(feature = "non_strict_integers")]
588 Self::UInt64 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
589 utoipa::openapi::schema::KnownFormat::UInt64
590 ))),
591 Self::Float => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
592 utoipa::openapi::schema::KnownFormat::Float
593 ))),
594 Self::Double => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
595 utoipa::openapi::schema::KnownFormat::Double
596 ))),
597 Self::Byte => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
598 utoipa::openapi::schema::KnownFormat::Byte
599 ))),
600 Self::Binary => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
601 utoipa::openapi::schema::KnownFormat::Binary
602 ))),
603 Self::Date => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
604 utoipa::openapi::schema::KnownFormat::Date
605 ))),
606 Self::DateTime => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
607 utoipa::openapi::schema::KnownFormat::DateTime
608 ))),
609 Self::Duration => tokens.extend(quote! {utoipa::openapi::schema::SchemaFormat::KnownFormat(
610 utoipa::openapi::schema::KnownFormat::Duration
611 ) }),
612 Self::Password => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
613 utoipa::openapi::schema::KnownFormat::Password
614 ))),
615 #[cfg(feature = "uuid")]
616 Self::Uuid => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
617 utoipa::openapi::schema::KnownFormat::Uuid
618 ))),
619 #[cfg(feature = "ulid")]
620 Self::Ulid => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
621 utoipa::openapi::schema::KnownFormat::Ulid
622 ))),
623 #[cfg(feature = "url")]
624 Self::Uri => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
625 utoipa::openapi::schema::KnownFormat::Uri
626 ))),
627 #[cfg(feature = "url")]
628 Self::UriReference => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
629 utoipa::openapi::schema::KnownFormat::UriReference
630 ))),
631 #[cfg(feature = "url")]
632 Self::Iri => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
633 utoipa::openapi::schema::KnownFormat::Iri
634 ))),
635 #[cfg(feature = "url")]
636 Self::IriReference => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
637 utoipa::openapi::schema::KnownFormat::IriReference
638 ))),
639 Self::Email => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
640 utoipa::openapi::schema::KnownFormat::Email
641 ))),
642 Self::IdnEmail => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
643 utoipa::openapi::schema::KnownFormat::IdnEmail
644 ))),
645 Self::Hostname => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
646 utoipa::openapi::schema::KnownFormat::Hostname
647 ))),
648 Self::IdnHostname => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
649 utoipa::openapi::schema::KnownFormat::IdnHostname
650 ))),
651 Self::Ipv4 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
652 utoipa::openapi::schema::KnownFormat::Ipv4
653 ))),
654 Self::Ipv6 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
655 utoipa::openapi::schema::KnownFormat::Ipv6
656 ))),
657 Self::UriTemplate => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
658 utoipa::openapi::schema::KnownFormat::UriTemplate
659 ))),
660 Self::JsonPointer => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
661 utoipa::openapi::schema::KnownFormat::JsonPointer
662 ))),
663 Self::RelativeJsonPointer => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
664 utoipa::openapi::schema::KnownFormat::RelativeJsonPointer
665 ))),
666 Self::Regex => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::KnownFormat(
667 utoipa::openapi::schema::KnownFormat::Regex
668 ))),
669 Self::Custom(value) => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Custom(
670 String::from(#value)
671 ))),
672 Self::Unknown => (), };
674 }
675}
676
677#[cfg_attr(feature = "debug", derive(Debug))]
678pub struct PrimitiveType {
679 pub ty: syn::Type,
680}
681
682impl PrimitiveType {
683 pub fn new(path: &Path) -> Option<PrimitiveType> {
684 let last_segment = path.segments.last().unwrap_or_else(|| {
685 panic!(
686 "Path for DefaultType must have at least one segment: `{path}`",
687 path = path.to_token_stream()
688 )
689 });
690
691 let name = &*last_segment.ident.to_string();
692
693 let ty: syn::Type = match name {
694 "String" | "str" | "char" => syn::parse_quote!(#path),
695
696 "bool" => syn::parse_quote!(#path),
697
698 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
699 | "u128" | "usize" => syn::parse_quote!(#path),
700 "f32" | "f64" => syn::parse_quote!(#path),
701
702 #[cfg(feature = "chrono")]
703 "DateTime" | "NaiveDateTime" | "NaiveDate" | "NaiveTime" => {
704 syn::parse_quote!(String)
705 }
706
707 #[cfg(any(feature = "chrono", feature = "time", feature = "jiff_0_2"))]
708 "Date" => {
709 syn::parse_quote!(String)
710 }
711
712 #[cfg(any(feature = "chrono", feature = "time"))]
713 "Duration" => {
714 syn::parse_quote!(String)
715 }
716
717 #[cfg(feature = "decimal")]
718 "Decimal" => {
719 syn::parse_quote!(String)
720 }
721
722 #[cfg(feature = "decimal_float")]
723 "Decimal" => {
724 syn::parse_quote!(f64)
725 }
726
727 #[cfg(feature = "rocket_extras")]
728 "PathBuf" => {
729 syn::parse_quote!(String)
730 }
731
732 #[cfg(feature = "uuid")]
733 "Uuid" => {
734 syn::parse_quote!(String)
735 }
736
737 #[cfg(feature = "ulid")]
738 "Ulid" => {
739 syn::parse_quote!(String)
740 }
741
742 #[cfg(feature = "url")]
743 "Url" => {
744 syn::parse_quote!(String)
745 }
746
747 #[cfg(feature = "time")]
748 "PrimitiveDateTime" | "OffsetDateTime" => {
749 syn::parse_quote!(String)
750 }
751
752 #[cfg(feature = "jiff_0_2")]
753 "Zoned" => {
754 syn::parse_quote!(String)
755 }
756 _ => {
757 return None;
759 }
760 };
761
762 Some(Self { ty })
763 }
764}