1use serde::{Deserialize, Serialize};
4use smol_str::SmolStr;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub struct Span {
9 pub start: usize,
11 pub end: usize,
13}
14
15impl Span {
16 pub fn new(start: usize, end: usize) -> Self {
18 Self { start, end }
19 }
20
21 pub fn len(&self) -> usize {
23 self.end - self.start
24 }
25
26 pub fn is_empty(&self) -> bool {
28 self.start == self.end
29 }
30
31 pub fn merge(self, other: Span) -> Span {
33 Span {
34 start: self.start.min(other.start),
35 end: self.end.max(other.end),
36 }
37 }
38}
39
40impl From<(usize, usize)> for Span {
41 fn from((start, end): (usize, usize)) -> Self {
42 Self { start, end }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48pub struct Ident {
49 pub name: SmolStr,
51 pub span: Span,
53}
54
55impl Ident {
56 pub fn new(name: impl Into<SmolStr>, span: Span) -> Self {
58 Self {
59 name: name.into(),
60 span,
61 }
62 }
63
64 pub fn as_str(&self) -> &str {
66 &self.name
67 }
68}
69
70impl std::fmt::Display for Ident {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{}", self.name)
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub enum ScalarType {
79 Int,
81 BigInt,
83 Float,
85 Decimal,
87 String,
89 Boolean,
91 DateTime,
93 Date,
95 Time,
97 Json,
99 Bytes,
101 Uuid,
103 Cuid,
105 Cuid2,
107 NanoId,
109 Ulid,
111}
112
113impl ScalarType {
114 #[allow(clippy::should_implement_trait)]
116 pub fn from_str(s: &str) -> Option<Self> {
117 match s {
118 "Int" => Some(Self::Int),
119 "BigInt" => Some(Self::BigInt),
120 "Float" => Some(Self::Float),
121 "Decimal" => Some(Self::Decimal),
122 "String" => Some(Self::String),
123 "Boolean" | "Bool" => Some(Self::Boolean),
124 "DateTime" => Some(Self::DateTime),
125 "Date" => Some(Self::Date),
126 "Time" => Some(Self::Time),
127 "Json" => Some(Self::Json),
128 "Bytes" => Some(Self::Bytes),
129 "Uuid" | "UUID" => Some(Self::Uuid),
130 "Cuid" | "CUID" => Some(Self::Cuid),
131 "Cuid2" | "CUID2" => Some(Self::Cuid2),
132 "NanoId" | "NanoID" | "Nanoid" => Some(Self::NanoId),
133 "Ulid" | "ULID" => Some(Self::Ulid),
134 _ => None,
135 }
136 }
137
138 pub fn as_str(&self) -> &'static str {
140 match self {
141 Self::Int => "Int",
142 Self::BigInt => "BigInt",
143 Self::Float => "Float",
144 Self::Decimal => "Decimal",
145 Self::String => "String",
146 Self::Boolean => "Boolean",
147 Self::DateTime => "DateTime",
148 Self::Date => "Date",
149 Self::Time => "Time",
150 Self::Json => "Json",
151 Self::Bytes => "Bytes",
152 Self::Uuid => "Uuid",
153 Self::Cuid => "Cuid",
154 Self::Cuid2 => "Cuid2",
155 Self::NanoId => "NanoId",
156 Self::Ulid => "Ulid",
157 }
158 }
159
160 pub fn is_id_type(&self) -> bool {
162 matches!(
163 self,
164 Self::Uuid | Self::Cuid | Self::Cuid2 | Self::NanoId | Self::Ulid
165 )
166 }
167}
168
169impl std::fmt::Display for ScalarType {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 write!(f, "{}", self.as_str())
172 }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
177pub enum FieldType {
178 Scalar(ScalarType),
180 Enum(SmolStr),
182 Model(SmolStr),
184 Composite(SmolStr),
186 Unsupported(SmolStr),
188}
189
190impl FieldType {
191 pub fn is_scalar(&self) -> bool {
193 matches!(self, Self::Scalar(_))
194 }
195
196 pub fn is_relation(&self) -> bool {
198 matches!(self, Self::Model(_))
199 }
200
201 pub fn is_enum(&self) -> bool {
203 matches!(self, Self::Enum(_))
204 }
205
206 pub fn type_name(&self) -> &str {
208 match self {
209 Self::Scalar(s) => s.as_str(),
210 Self::Enum(name)
211 | Self::Model(name)
212 | Self::Composite(name)
213 | Self::Unsupported(name) => name.as_str(),
214 }
215 }
216}
217
218impl std::fmt::Display for FieldType {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "{}", self.type_name())
221 }
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
226pub enum TypeModifier {
227 Required,
229 Optional,
231 List,
233 OptionalList,
235}
236
237impl TypeModifier {
238 pub fn is_optional(&self) -> bool {
240 matches!(self, Self::Optional | Self::OptionalList)
241 }
242
243 pub fn is_list(&self) -> bool {
245 matches!(self, Self::List | Self::OptionalList)
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
251pub struct Documentation {
252 pub text: String,
254 pub span: Span,
256}
257
258impl Documentation {
259 pub fn new(text: impl Into<String>, span: Span) -> Self {
261 Self {
262 text: text.into(),
263 span,
264 }
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
275 fn test_span_new() {
276 let span = Span::new(10, 20);
277 assert_eq!(span.start, 10);
278 assert_eq!(span.end, 20);
279 }
280
281 #[test]
282 fn test_span_len() {
283 let span = Span::new(5, 15);
284 assert_eq!(span.len(), 10);
285 }
286
287 #[test]
288 fn test_span_len_zero() {
289 let span = Span::new(10, 10);
290 assert_eq!(span.len(), 0);
291 }
292
293 #[test]
294 fn test_span_is_empty_true() {
295 let span = Span::new(5, 5);
296 assert!(span.is_empty());
297 }
298
299 #[test]
300 fn test_span_is_empty_false() {
301 let span = Span::new(5, 10);
302 assert!(!span.is_empty());
303 }
304
305 #[test]
306 fn test_span_merge_adjacent() {
307 let span1 = Span::new(0, 10);
308 let span2 = Span::new(10, 20);
309 let merged = span1.merge(span2);
310 assert_eq!(merged.start, 0);
311 assert_eq!(merged.end, 20);
312 }
313
314 #[test]
315 fn test_span_merge_overlapping() {
316 let span1 = Span::new(5, 15);
317 let span2 = Span::new(10, 25);
318 let merged = span1.merge(span2);
319 assert_eq!(merged.start, 5);
320 assert_eq!(merged.end, 25);
321 }
322
323 #[test]
324 fn test_span_merge_disjoint() {
325 let span1 = Span::new(0, 5);
326 let span2 = Span::new(20, 30);
327 let merged = span1.merge(span2);
328 assert_eq!(merged.start, 0);
329 assert_eq!(merged.end, 30);
330 }
331
332 #[test]
333 fn test_span_from_tuple() {
334 let span: Span = (10, 20).into();
335 assert_eq!(span.start, 10);
336 assert_eq!(span.end, 20);
337 }
338
339 #[test]
340 fn test_span_equality() {
341 let span1 = Span::new(10, 20);
342 let span2 = Span::new(10, 20);
343 let span3 = Span::new(10, 25);
344 assert_eq!(span1, span2);
345 assert_ne!(span1, span3);
346 }
347
348 #[test]
349 fn test_span_clone() {
350 let span1 = Span::new(10, 20);
351 let span2 = span1;
352 assert_eq!(span1, span2);
353 }
354
355 #[test]
358 fn test_ident_new() {
359 let ident = Ident::new("user_id", Span::new(0, 7));
360 assert_eq!(ident.name.as_str(), "user_id");
361 assert_eq!(ident.span.start, 0);
362 assert_eq!(ident.span.end, 7);
363 }
364
365 #[test]
366 fn test_ident_as_str() {
367 let ident = Ident::new("field_name", Span::new(0, 10));
368 assert_eq!(ident.as_str(), "field_name");
369 }
370
371 #[test]
372 fn test_ident_display() {
373 let ident = Ident::new("MyModel", Span::new(0, 7));
374 assert_eq!(format!("{}", ident), "MyModel");
375 }
376
377 #[test]
378 fn test_ident_equality() {
379 let ident1 = Ident::new("name", Span::new(0, 4));
380 let ident2 = Ident::new("name", Span::new(0, 4));
381 let ident3 = Ident::new("name", Span::new(5, 9));
382 let ident4 = Ident::new("other", Span::new(0, 5));
383
384 assert_eq!(ident1, ident2);
385 assert_ne!(ident1, ident3); assert_ne!(ident1, ident4); }
388
389 #[test]
390 fn test_ident_from_string() {
391 let name = String::from("dynamic_name");
392 let ident = Ident::new(name, Span::new(0, 12));
393 assert_eq!(ident.as_str(), "dynamic_name");
394 }
395
396 #[test]
399 fn test_scalar_type_from_str_int() {
400 assert_eq!(ScalarType::from_str("Int"), Some(ScalarType::Int));
401 }
402
403 #[test]
404 fn test_scalar_type_from_str_bigint() {
405 assert_eq!(ScalarType::from_str("BigInt"), Some(ScalarType::BigInt));
406 }
407
408 #[test]
409 fn test_scalar_type_from_str_float() {
410 assert_eq!(ScalarType::from_str("Float"), Some(ScalarType::Float));
411 }
412
413 #[test]
414 fn test_scalar_type_from_str_decimal() {
415 assert_eq!(ScalarType::from_str("Decimal"), Some(ScalarType::Decimal));
416 }
417
418 #[test]
419 fn test_scalar_type_from_str_string() {
420 assert_eq!(ScalarType::from_str("String"), Some(ScalarType::String));
421 }
422
423 #[test]
424 fn test_scalar_type_from_str_boolean() {
425 assert_eq!(ScalarType::from_str("Boolean"), Some(ScalarType::Boolean));
426 }
427
428 #[test]
429 fn test_scalar_type_from_str_bool_alias() {
430 assert_eq!(ScalarType::from_str("Bool"), Some(ScalarType::Boolean));
431 }
432
433 #[test]
434 fn test_scalar_type_from_str_datetime() {
435 assert_eq!(ScalarType::from_str("DateTime"), Some(ScalarType::DateTime));
436 }
437
438 #[test]
439 fn test_scalar_type_from_str_date() {
440 assert_eq!(ScalarType::from_str("Date"), Some(ScalarType::Date));
441 }
442
443 #[test]
444 fn test_scalar_type_from_str_time() {
445 assert_eq!(ScalarType::from_str("Time"), Some(ScalarType::Time));
446 }
447
448 #[test]
449 fn test_scalar_type_from_str_json() {
450 assert_eq!(ScalarType::from_str("Json"), Some(ScalarType::Json));
451 }
452
453 #[test]
454 fn test_scalar_type_from_str_bytes() {
455 assert_eq!(ScalarType::from_str("Bytes"), Some(ScalarType::Bytes));
456 }
457
458 #[test]
459 fn test_scalar_type_from_str_uuid() {
460 assert_eq!(ScalarType::from_str("Uuid"), Some(ScalarType::Uuid));
461 }
462
463 #[test]
464 fn test_scalar_type_from_str_uuid_uppercase() {
465 assert_eq!(ScalarType::from_str("UUID"), Some(ScalarType::Uuid));
466 }
467
468 #[test]
469 fn test_scalar_type_from_str_cuid() {
470 assert_eq!(ScalarType::from_str("Cuid"), Some(ScalarType::Cuid));
471 assert_eq!(ScalarType::from_str("CUID"), Some(ScalarType::Cuid));
472 }
473
474 #[test]
475 fn test_scalar_type_from_str_cuid2() {
476 assert_eq!(ScalarType::from_str("Cuid2"), Some(ScalarType::Cuid2));
477 assert_eq!(ScalarType::from_str("CUID2"), Some(ScalarType::Cuid2));
478 }
479
480 #[test]
481 fn test_scalar_type_from_str_nanoid() {
482 assert_eq!(ScalarType::from_str("NanoId"), Some(ScalarType::NanoId));
483 assert_eq!(ScalarType::from_str("NanoID"), Some(ScalarType::NanoId));
484 assert_eq!(ScalarType::from_str("Nanoid"), Some(ScalarType::NanoId));
485 }
486
487 #[test]
488 fn test_scalar_type_from_str_ulid() {
489 assert_eq!(ScalarType::from_str("Ulid"), Some(ScalarType::Ulid));
490 assert_eq!(ScalarType::from_str("ULID"), Some(ScalarType::Ulid));
491 }
492
493 #[test]
494 fn test_scalar_type_from_str_unknown() {
495 assert_eq!(ScalarType::from_str("Unknown"), None);
496 assert_eq!(ScalarType::from_str("int"), None); assert_eq!(ScalarType::from_str(""), None);
498 }
499
500 #[test]
501 fn test_scalar_type_as_str() {
502 assert_eq!(ScalarType::Int.as_str(), "Int");
503 assert_eq!(ScalarType::BigInt.as_str(), "BigInt");
504 assert_eq!(ScalarType::Float.as_str(), "Float");
505 assert_eq!(ScalarType::Decimal.as_str(), "Decimal");
506 assert_eq!(ScalarType::String.as_str(), "String");
507 assert_eq!(ScalarType::Boolean.as_str(), "Boolean");
508 assert_eq!(ScalarType::DateTime.as_str(), "DateTime");
509 assert_eq!(ScalarType::Date.as_str(), "Date");
510 assert_eq!(ScalarType::Time.as_str(), "Time");
511 assert_eq!(ScalarType::Json.as_str(), "Json");
512 assert_eq!(ScalarType::Bytes.as_str(), "Bytes");
513 assert_eq!(ScalarType::Uuid.as_str(), "Uuid");
514 assert_eq!(ScalarType::Cuid.as_str(), "Cuid");
515 assert_eq!(ScalarType::Cuid2.as_str(), "Cuid2");
516 assert_eq!(ScalarType::NanoId.as_str(), "NanoId");
517 assert_eq!(ScalarType::Ulid.as_str(), "Ulid");
518 }
519
520 #[test]
521 fn test_scalar_type_is_id_type() {
522 assert!(ScalarType::Uuid.is_id_type());
523 assert!(ScalarType::Cuid.is_id_type());
524 assert!(ScalarType::Cuid2.is_id_type());
525 assert!(ScalarType::NanoId.is_id_type());
526 assert!(ScalarType::Ulid.is_id_type());
527 assert!(!ScalarType::Int.is_id_type());
528 assert!(!ScalarType::String.is_id_type());
529 }
530
531 #[test]
532 fn test_scalar_type_display() {
533 assert_eq!(format!("{}", ScalarType::Int), "Int");
534 assert_eq!(format!("{}", ScalarType::String), "String");
535 assert_eq!(format!("{}", ScalarType::DateTime), "DateTime");
536 }
537
538 #[test]
539 fn test_scalar_type_equality() {
540 assert_eq!(ScalarType::Int, ScalarType::Int);
541 assert_ne!(ScalarType::Int, ScalarType::String);
542 }
543
544 #[test]
547 fn test_field_type_scalar() {
548 let ft = FieldType::Scalar(ScalarType::Int);
549 assert!(ft.is_scalar());
550 assert!(!ft.is_relation());
551 assert!(!ft.is_enum());
552 assert_eq!(ft.type_name(), "Int");
553 }
554
555 #[test]
556 fn test_field_type_enum() {
557 let ft = FieldType::Enum("Role".into());
558 assert!(!ft.is_scalar());
559 assert!(!ft.is_relation());
560 assert!(ft.is_enum());
561 assert_eq!(ft.type_name(), "Role");
562 }
563
564 #[test]
565 fn test_field_type_model() {
566 let ft = FieldType::Model("User".into());
567 assert!(!ft.is_scalar());
568 assert!(ft.is_relation());
569 assert!(!ft.is_enum());
570 assert_eq!(ft.type_name(), "User");
571 }
572
573 #[test]
574 fn test_field_type_composite() {
575 let ft = FieldType::Composite("Address".into());
576 assert!(!ft.is_scalar());
577 assert!(!ft.is_relation());
578 assert!(!ft.is_enum());
579 assert_eq!(ft.type_name(), "Address");
580 }
581
582 #[test]
583 fn test_field_type_unsupported() {
584 let ft = FieldType::Unsupported("CustomType".into());
585 assert!(!ft.is_scalar());
586 assert!(!ft.is_relation());
587 assert!(!ft.is_enum());
588 assert_eq!(ft.type_name(), "CustomType");
589 }
590
591 #[test]
592 fn test_field_type_display() {
593 assert_eq!(
594 format!("{}", FieldType::Scalar(ScalarType::String)),
595 "String"
596 );
597 assert_eq!(format!("{}", FieldType::Enum("Status".into())), "Status");
598 assert_eq!(format!("{}", FieldType::Model("Post".into())), "Post");
599 }
600
601 #[test]
602 fn test_field_type_equality() {
603 let ft1 = FieldType::Scalar(ScalarType::Int);
604 let ft2 = FieldType::Scalar(ScalarType::Int);
605 let ft3 = FieldType::Scalar(ScalarType::String);
606
607 assert_eq!(ft1, ft2);
608 assert_ne!(ft1, ft3);
609 }
610
611 #[test]
614 fn test_type_modifier_required() {
615 let tm = TypeModifier::Required;
616 assert!(!tm.is_optional());
617 assert!(!tm.is_list());
618 }
619
620 #[test]
621 fn test_type_modifier_optional() {
622 let tm = TypeModifier::Optional;
623 assert!(tm.is_optional());
624 assert!(!tm.is_list());
625 }
626
627 #[test]
628 fn test_type_modifier_list() {
629 let tm = TypeModifier::List;
630 assert!(!tm.is_optional());
631 assert!(tm.is_list());
632 }
633
634 #[test]
635 fn test_type_modifier_optional_list() {
636 let tm = TypeModifier::OptionalList;
637 assert!(tm.is_optional());
638 assert!(tm.is_list());
639 }
640
641 #[test]
642 fn test_type_modifier_equality() {
643 assert_eq!(TypeModifier::Required, TypeModifier::Required);
644 assert_eq!(TypeModifier::Optional, TypeModifier::Optional);
645 assert_ne!(TypeModifier::Required, TypeModifier::Optional);
646 }
647
648 #[test]
651 fn test_documentation_new() {
652 let doc = Documentation::new("This is a doc comment", Span::new(0, 21));
653 assert_eq!(doc.text, "This is a doc comment");
654 assert_eq!(doc.span.start, 0);
655 assert_eq!(doc.span.end, 21);
656 }
657
658 #[test]
659 fn test_documentation_from_string() {
660 let text = String::from("Dynamic documentation");
661 let doc = Documentation::new(text, Span::new(0, 21));
662 assert_eq!(doc.text, "Dynamic documentation");
663 }
664
665 #[test]
666 fn test_documentation_equality() {
667 let doc1 = Documentation::new("Same text", Span::new(0, 9));
668 let doc2 = Documentation::new("Same text", Span::new(0, 9));
669 let doc3 = Documentation::new("Different", Span::new(0, 9));
670
671 assert_eq!(doc1, doc2);
672 assert_ne!(doc1, doc3);
673 }
674
675 #[test]
676 fn test_documentation_multiline() {
677 let doc = Documentation::new("Line 1\nLine 2\nLine 3", Span::new(0, 20));
678 assert!(doc.text.contains('\n'));
679 assert!(doc.text.starts_with("Line 1"));
680 assert!(doc.text.ends_with("Line 3"));
681 }
682}