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 Vector(Option<u32>),
118 HalfVector(Option<u32>),
122 SparseVector(Option<u32>),
126 Bit(Option<u32>),
130}
131
132impl ScalarType {
133 #[allow(clippy::should_implement_trait)]
136 pub fn from_str(s: &str) -> Option<Self> {
137 match s {
138 "Int" => Some(Self::Int),
139 "BigInt" => Some(Self::BigInt),
140 "Float" => Some(Self::Float),
141 "Decimal" => Some(Self::Decimal),
142 "String" => Some(Self::String),
143 "Boolean" | "Bool" => Some(Self::Boolean),
144 "DateTime" => Some(Self::DateTime),
145 "Date" => Some(Self::Date),
146 "Time" => Some(Self::Time),
147 "Json" => Some(Self::Json),
148 "Bytes" => Some(Self::Bytes),
149 "Uuid" | "UUID" => Some(Self::Uuid),
150 "Cuid" | "CUID" => Some(Self::Cuid),
151 "Cuid2" | "CUID2" => Some(Self::Cuid2),
152 "NanoId" | "NanoID" | "Nanoid" => Some(Self::NanoId),
153 "Ulid" | "ULID" => Some(Self::Ulid),
154 "Vector" => Some(Self::Vector(None)),
156 "HalfVector" | "Halfvec" => Some(Self::HalfVector(None)),
157 "SparseVector" | "Sparsevec" => Some(Self::SparseVector(None)),
158 "Bit" => Some(Self::Bit(None)),
159 _ => None,
160 }
161 }
162
163 pub fn parse_with_param(name: &str, param: Option<u32>) -> Option<Self> {
166 match name {
167 "Vector" => Some(Self::Vector(param)),
168 "HalfVector" | "Halfvec" => Some(Self::HalfVector(param)),
169 "SparseVector" | "Sparsevec" => Some(Self::SparseVector(param)),
170 "Bit" => Some(Self::Bit(param)),
171 _ => Self::from_str(name),
172 }
173 }
174
175 pub fn as_str(&self) -> &'static str {
177 match self {
178 Self::Int => "Int",
179 Self::BigInt => "BigInt",
180 Self::Float => "Float",
181 Self::Decimal => "Decimal",
182 Self::String => "String",
183 Self::Boolean => "Boolean",
184 Self::DateTime => "DateTime",
185 Self::Date => "Date",
186 Self::Time => "Time",
187 Self::Json => "Json",
188 Self::Bytes => "Bytes",
189 Self::Uuid => "Uuid",
190 Self::Cuid => "Cuid",
191 Self::Cuid2 => "Cuid2",
192 Self::NanoId => "NanoId",
193 Self::Ulid => "Ulid",
194 Self::Vector(_) => "Vector",
195 Self::HalfVector(_) => "HalfVector",
196 Self::SparseVector(_) => "SparseVector",
197 Self::Bit(_) => "Bit",
198 }
199 }
200
201 pub fn is_id_type(&self) -> bool {
203 matches!(
204 self,
205 Self::Uuid | Self::Cuid | Self::Cuid2 | Self::NanoId | Self::Ulid
206 )
207 }
208
209 pub fn requires_vector_extension(&self) -> bool {
211 matches!(
212 self,
213 Self::Vector(_) | Self::HalfVector(_) | Self::SparseVector(_) | Self::Bit(_)
214 )
215 }
216
217 pub fn dimension(&self) -> Option<u32> {
219 match self {
220 Self::Vector(d) | Self::HalfVector(d) | Self::SparseVector(d) | Self::Bit(d) => *d,
221 _ => None,
222 }
223 }
224
225 pub fn postgres_type(&self) -> String {
227 match self {
228 Self::Int => "INTEGER".to_string(),
229 Self::BigInt => "BIGINT".to_string(),
230 Self::Float => "DOUBLE PRECISION".to_string(),
231 Self::Decimal => "DECIMAL".to_string(),
232 Self::String => "TEXT".to_string(),
233 Self::Boolean => "BOOLEAN".to_string(),
234 Self::DateTime => "TIMESTAMP WITH TIME ZONE".to_string(),
235 Self::Date => "DATE".to_string(),
236 Self::Time => "TIME".to_string(),
237 Self::Json => "JSONB".to_string(),
238 Self::Bytes => "BYTEA".to_string(),
239 Self::Uuid => "UUID".to_string(),
240 Self::Cuid | Self::Cuid2 | Self::NanoId | Self::Ulid => "TEXT".to_string(),
241 Self::Vector(Some(dim)) => format!("vector({})", dim),
242 Self::Vector(None) => "vector".to_string(),
243 Self::HalfVector(Some(dim)) => format!("halfvec({})", dim),
244 Self::HalfVector(None) => "halfvec".to_string(),
245 Self::SparseVector(Some(dim)) => format!("sparsevec({})", dim),
246 Self::SparseVector(None) => "sparsevec".to_string(),
247 Self::Bit(Some(dim)) => format!("bit({})", dim),
248 Self::Bit(None) => "bit".to_string(),
249 }
250 }
251}
252
253impl std::fmt::Display for ScalarType {
254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 write!(f, "{}", self.as_str())
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
261pub enum FieldType {
262 Scalar(ScalarType),
264 Enum(SmolStr),
266 Model(SmolStr),
268 Composite(SmolStr),
270 Unsupported(SmolStr),
272}
273
274impl FieldType {
275 pub fn is_scalar(&self) -> bool {
277 matches!(self, Self::Scalar(_))
278 }
279
280 pub fn is_relation(&self) -> bool {
282 matches!(self, Self::Model(_))
283 }
284
285 pub fn is_enum(&self) -> bool {
287 matches!(self, Self::Enum(_))
288 }
289
290 pub fn type_name(&self) -> &str {
292 match self {
293 Self::Scalar(s) => s.as_str(),
294 Self::Enum(name)
295 | Self::Model(name)
296 | Self::Composite(name)
297 | Self::Unsupported(name) => name.as_str(),
298 }
299 }
300}
301
302impl std::fmt::Display for FieldType {
303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 write!(f, "{}", self.type_name())
305 }
306}
307
308#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
310pub enum TypeModifier {
311 Required,
313 Optional,
315 List,
317 OptionalList,
319}
320
321impl TypeModifier {
322 pub fn is_optional(&self) -> bool {
324 matches!(self, Self::Optional | Self::OptionalList)
325 }
326
327 pub fn is_list(&self) -> bool {
329 matches!(self, Self::List | Self::OptionalList)
330 }
331}
332
333#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335pub struct Documentation {
336 pub text: String,
338 pub span: Span,
340}
341
342impl Documentation {
343 pub fn new(text: impl Into<String>, span: Span) -> Self {
345 Self {
346 text: text.into(),
347 span,
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
359 fn test_span_new() {
360 let span = Span::new(10, 20);
361 assert_eq!(span.start, 10);
362 assert_eq!(span.end, 20);
363 }
364
365 #[test]
366 fn test_span_len() {
367 let span = Span::new(5, 15);
368 assert_eq!(span.len(), 10);
369 }
370
371 #[test]
372 fn test_span_len_zero() {
373 let span = Span::new(10, 10);
374 assert_eq!(span.len(), 0);
375 }
376
377 #[test]
378 fn test_span_is_empty_true() {
379 let span = Span::new(5, 5);
380 assert!(span.is_empty());
381 }
382
383 #[test]
384 fn test_span_is_empty_false() {
385 let span = Span::new(5, 10);
386 assert!(!span.is_empty());
387 }
388
389 #[test]
390 fn test_span_merge_adjacent() {
391 let span1 = Span::new(0, 10);
392 let span2 = Span::new(10, 20);
393 let merged = span1.merge(span2);
394 assert_eq!(merged.start, 0);
395 assert_eq!(merged.end, 20);
396 }
397
398 #[test]
399 fn test_span_merge_overlapping() {
400 let span1 = Span::new(5, 15);
401 let span2 = Span::new(10, 25);
402 let merged = span1.merge(span2);
403 assert_eq!(merged.start, 5);
404 assert_eq!(merged.end, 25);
405 }
406
407 #[test]
408 fn test_span_merge_disjoint() {
409 let span1 = Span::new(0, 5);
410 let span2 = Span::new(20, 30);
411 let merged = span1.merge(span2);
412 assert_eq!(merged.start, 0);
413 assert_eq!(merged.end, 30);
414 }
415
416 #[test]
417 fn test_span_from_tuple() {
418 let span: Span = (10, 20).into();
419 assert_eq!(span.start, 10);
420 assert_eq!(span.end, 20);
421 }
422
423 #[test]
424 fn test_span_equality() {
425 let span1 = Span::new(10, 20);
426 let span2 = Span::new(10, 20);
427 let span3 = Span::new(10, 25);
428 assert_eq!(span1, span2);
429 assert_ne!(span1, span3);
430 }
431
432 #[test]
433 fn test_span_clone() {
434 let span1 = Span::new(10, 20);
435 let span2 = span1;
436 assert_eq!(span1, span2);
437 }
438
439 #[test]
442 fn test_ident_new() {
443 let ident = Ident::new("user_id", Span::new(0, 7));
444 assert_eq!(ident.name.as_str(), "user_id");
445 assert_eq!(ident.span.start, 0);
446 assert_eq!(ident.span.end, 7);
447 }
448
449 #[test]
450 fn test_ident_as_str() {
451 let ident = Ident::new("field_name", Span::new(0, 10));
452 assert_eq!(ident.as_str(), "field_name");
453 }
454
455 #[test]
456 fn test_ident_display() {
457 let ident = Ident::new("MyModel", Span::new(0, 7));
458 assert_eq!(format!("{}", ident), "MyModel");
459 }
460
461 #[test]
462 fn test_ident_equality() {
463 let ident1 = Ident::new("name", Span::new(0, 4));
464 let ident2 = Ident::new("name", Span::new(0, 4));
465 let ident3 = Ident::new("name", Span::new(5, 9));
466 let ident4 = Ident::new("other", Span::new(0, 5));
467
468 assert_eq!(ident1, ident2);
469 assert_ne!(ident1, ident3); assert_ne!(ident1, ident4); }
472
473 #[test]
474 fn test_ident_from_string() {
475 let name = String::from("dynamic_name");
476 let ident = Ident::new(name, Span::new(0, 12));
477 assert_eq!(ident.as_str(), "dynamic_name");
478 }
479
480 #[test]
483 fn test_scalar_type_from_str_int() {
484 assert_eq!(ScalarType::from_str("Int"), Some(ScalarType::Int));
485 }
486
487 #[test]
488 fn test_scalar_type_from_str_bigint() {
489 assert_eq!(ScalarType::from_str("BigInt"), Some(ScalarType::BigInt));
490 }
491
492 #[test]
493 fn test_scalar_type_from_str_float() {
494 assert_eq!(ScalarType::from_str("Float"), Some(ScalarType::Float));
495 }
496
497 #[test]
498 fn test_scalar_type_from_str_decimal() {
499 assert_eq!(ScalarType::from_str("Decimal"), Some(ScalarType::Decimal));
500 }
501
502 #[test]
503 fn test_scalar_type_from_str_string() {
504 assert_eq!(ScalarType::from_str("String"), Some(ScalarType::String));
505 }
506
507 #[test]
508 fn test_scalar_type_from_str_boolean() {
509 assert_eq!(ScalarType::from_str("Boolean"), Some(ScalarType::Boolean));
510 }
511
512 #[test]
513 fn test_scalar_type_from_str_bool_alias() {
514 assert_eq!(ScalarType::from_str("Bool"), Some(ScalarType::Boolean));
515 }
516
517 #[test]
518 fn test_scalar_type_from_str_datetime() {
519 assert_eq!(ScalarType::from_str("DateTime"), Some(ScalarType::DateTime));
520 }
521
522 #[test]
523 fn test_scalar_type_from_str_date() {
524 assert_eq!(ScalarType::from_str("Date"), Some(ScalarType::Date));
525 }
526
527 #[test]
528 fn test_scalar_type_from_str_time() {
529 assert_eq!(ScalarType::from_str("Time"), Some(ScalarType::Time));
530 }
531
532 #[test]
533 fn test_scalar_type_from_str_json() {
534 assert_eq!(ScalarType::from_str("Json"), Some(ScalarType::Json));
535 }
536
537 #[test]
538 fn test_scalar_type_from_str_bytes() {
539 assert_eq!(ScalarType::from_str("Bytes"), Some(ScalarType::Bytes));
540 }
541
542 #[test]
543 fn test_scalar_type_from_str_uuid() {
544 assert_eq!(ScalarType::from_str("Uuid"), Some(ScalarType::Uuid));
545 }
546
547 #[test]
548 fn test_scalar_type_from_str_uuid_uppercase() {
549 assert_eq!(ScalarType::from_str("UUID"), Some(ScalarType::Uuid));
550 }
551
552 #[test]
553 fn test_scalar_type_from_str_cuid() {
554 assert_eq!(ScalarType::from_str("Cuid"), Some(ScalarType::Cuid));
555 assert_eq!(ScalarType::from_str("CUID"), Some(ScalarType::Cuid));
556 }
557
558 #[test]
559 fn test_scalar_type_from_str_cuid2() {
560 assert_eq!(ScalarType::from_str("Cuid2"), Some(ScalarType::Cuid2));
561 assert_eq!(ScalarType::from_str("CUID2"), Some(ScalarType::Cuid2));
562 }
563
564 #[test]
565 fn test_scalar_type_from_str_nanoid() {
566 assert_eq!(ScalarType::from_str("NanoId"), Some(ScalarType::NanoId));
567 assert_eq!(ScalarType::from_str("NanoID"), Some(ScalarType::NanoId));
568 assert_eq!(ScalarType::from_str("Nanoid"), Some(ScalarType::NanoId));
569 }
570
571 #[test]
572 fn test_scalar_type_from_str_ulid() {
573 assert_eq!(ScalarType::from_str("Ulid"), Some(ScalarType::Ulid));
574 assert_eq!(ScalarType::from_str("ULID"), Some(ScalarType::Ulid));
575 }
576
577 #[test]
578 fn test_scalar_type_from_str_unknown() {
579 assert_eq!(ScalarType::from_str("Unknown"), None);
580 assert_eq!(ScalarType::from_str("int"), None); assert_eq!(ScalarType::from_str(""), None);
582 }
583
584 #[test]
585 fn test_scalar_type_as_str() {
586 assert_eq!(ScalarType::Int.as_str(), "Int");
587 assert_eq!(ScalarType::BigInt.as_str(), "BigInt");
588 assert_eq!(ScalarType::Float.as_str(), "Float");
589 assert_eq!(ScalarType::Decimal.as_str(), "Decimal");
590 assert_eq!(ScalarType::String.as_str(), "String");
591 assert_eq!(ScalarType::Boolean.as_str(), "Boolean");
592 assert_eq!(ScalarType::DateTime.as_str(), "DateTime");
593 assert_eq!(ScalarType::Date.as_str(), "Date");
594 assert_eq!(ScalarType::Time.as_str(), "Time");
595 assert_eq!(ScalarType::Json.as_str(), "Json");
596 assert_eq!(ScalarType::Bytes.as_str(), "Bytes");
597 assert_eq!(ScalarType::Uuid.as_str(), "Uuid");
598 assert_eq!(ScalarType::Cuid.as_str(), "Cuid");
599 assert_eq!(ScalarType::Cuid2.as_str(), "Cuid2");
600 assert_eq!(ScalarType::NanoId.as_str(), "NanoId");
601 assert_eq!(ScalarType::Ulid.as_str(), "Ulid");
602 }
603
604 #[test]
605 fn test_scalar_type_is_id_type() {
606 assert!(ScalarType::Uuid.is_id_type());
607 assert!(ScalarType::Cuid.is_id_type());
608 assert!(ScalarType::Cuid2.is_id_type());
609 assert!(ScalarType::NanoId.is_id_type());
610 assert!(ScalarType::Ulid.is_id_type());
611 assert!(!ScalarType::Int.is_id_type());
612 assert!(!ScalarType::String.is_id_type());
613 }
614
615 #[test]
616 fn test_scalar_type_display() {
617 assert_eq!(format!("{}", ScalarType::Int), "Int");
618 assert_eq!(format!("{}", ScalarType::String), "String");
619 assert_eq!(format!("{}", ScalarType::DateTime), "DateTime");
620 }
621
622 #[test]
623 fn test_scalar_type_equality() {
624 assert_eq!(ScalarType::Int, ScalarType::Int);
625 assert_ne!(ScalarType::Int, ScalarType::String);
626 }
627
628 #[test]
631 fn test_field_type_scalar() {
632 let ft = FieldType::Scalar(ScalarType::Int);
633 assert!(ft.is_scalar());
634 assert!(!ft.is_relation());
635 assert!(!ft.is_enum());
636 assert_eq!(ft.type_name(), "Int");
637 }
638
639 #[test]
640 fn test_field_type_enum() {
641 let ft = FieldType::Enum("Role".into());
642 assert!(!ft.is_scalar());
643 assert!(!ft.is_relation());
644 assert!(ft.is_enum());
645 assert_eq!(ft.type_name(), "Role");
646 }
647
648 #[test]
649 fn test_field_type_model() {
650 let ft = FieldType::Model("User".into());
651 assert!(!ft.is_scalar());
652 assert!(ft.is_relation());
653 assert!(!ft.is_enum());
654 assert_eq!(ft.type_name(), "User");
655 }
656
657 #[test]
658 fn test_field_type_composite() {
659 let ft = FieldType::Composite("Address".into());
660 assert!(!ft.is_scalar());
661 assert!(!ft.is_relation());
662 assert!(!ft.is_enum());
663 assert_eq!(ft.type_name(), "Address");
664 }
665
666 #[test]
667 fn test_field_type_unsupported() {
668 let ft = FieldType::Unsupported("CustomType".into());
669 assert!(!ft.is_scalar());
670 assert!(!ft.is_relation());
671 assert!(!ft.is_enum());
672 assert_eq!(ft.type_name(), "CustomType");
673 }
674
675 #[test]
676 fn test_field_type_display() {
677 assert_eq!(
678 format!("{}", FieldType::Scalar(ScalarType::String)),
679 "String"
680 );
681 assert_eq!(format!("{}", FieldType::Enum("Status".into())), "Status");
682 assert_eq!(format!("{}", FieldType::Model("Post".into())), "Post");
683 }
684
685 #[test]
686 fn test_field_type_equality() {
687 let ft1 = FieldType::Scalar(ScalarType::Int);
688 let ft2 = FieldType::Scalar(ScalarType::Int);
689 let ft3 = FieldType::Scalar(ScalarType::String);
690
691 assert_eq!(ft1, ft2);
692 assert_ne!(ft1, ft3);
693 }
694
695 #[test]
698 fn test_type_modifier_required() {
699 let tm = TypeModifier::Required;
700 assert!(!tm.is_optional());
701 assert!(!tm.is_list());
702 }
703
704 #[test]
705 fn test_type_modifier_optional() {
706 let tm = TypeModifier::Optional;
707 assert!(tm.is_optional());
708 assert!(!tm.is_list());
709 }
710
711 #[test]
712 fn test_type_modifier_list() {
713 let tm = TypeModifier::List;
714 assert!(!tm.is_optional());
715 assert!(tm.is_list());
716 }
717
718 #[test]
719 fn test_type_modifier_optional_list() {
720 let tm = TypeModifier::OptionalList;
721 assert!(tm.is_optional());
722 assert!(tm.is_list());
723 }
724
725 #[test]
726 fn test_type_modifier_equality() {
727 assert_eq!(TypeModifier::Required, TypeModifier::Required);
728 assert_eq!(TypeModifier::Optional, TypeModifier::Optional);
729 assert_ne!(TypeModifier::Required, TypeModifier::Optional);
730 }
731
732 #[test]
735 fn test_documentation_new() {
736 let doc = Documentation::new("This is a doc comment", Span::new(0, 21));
737 assert_eq!(doc.text, "This is a doc comment");
738 assert_eq!(doc.span.start, 0);
739 assert_eq!(doc.span.end, 21);
740 }
741
742 #[test]
743 fn test_documentation_from_string() {
744 let text = String::from("Dynamic documentation");
745 let doc = Documentation::new(text, Span::new(0, 21));
746 assert_eq!(doc.text, "Dynamic documentation");
747 }
748
749 #[test]
750 fn test_documentation_equality() {
751 let doc1 = Documentation::new("Same text", Span::new(0, 9));
752 let doc2 = Documentation::new("Same text", Span::new(0, 9));
753 let doc3 = Documentation::new("Different", Span::new(0, 9));
754
755 assert_eq!(doc1, doc2);
756 assert_ne!(doc1, doc3);
757 }
758
759 #[test]
760 fn test_documentation_multiline() {
761 let doc = Documentation::new("Line 1\nLine 2\nLine 3", Span::new(0, 20));
762 assert!(doc.text.contains('\n'));
763 assert!(doc.text.starts_with("Line 1"));
764 assert!(doc.text.ends_with("Line 3"));
765 }
766}