Skip to main content

surrealdb_types/kind/
mod.rs

1mod geometry;
2mod literal;
3
4use std::collections::HashSet;
5use std::fmt::Display;
6
7pub use geometry::*;
8pub use literal::*;
9use serde::{Deserialize, Serialize};
10use surrealdb_types_derive::write_sql;
11
12use crate::utils::display::format_seperated;
13use crate::{self as surrealdb_types, SqlFormat, Table, ToSql};
14
15/// The kind of a SurrealDB value.
16#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
17pub enum Kind {
18	/// The most generic type, can be anything.
19	#[default]
20	Any,
21	/// None type.
22	None,
23	/// Null type.
24	Null,
25	/// Boolean type.
26	Bool,
27	/// Bytes type.
28	Bytes,
29	/// Datetime type.
30	Datetime,
31	/// Decimal type.
32	Decimal,
33	/// Duration type.
34	Duration,
35	/// 64-bit floating point type.
36	Float,
37	/// 64-bit signed integer type.
38	Int,
39	/// Number type, can be either a float, int or decimal.
40	/// This is the most generic type for numbers.
41	Number,
42	/// Object type.
43	Object,
44	/// String type.
45	String,
46	/// UUID type.
47	Uuid,
48	/// Regular expression type.
49	Regex,
50	/// A table type.
51	Table(Vec<Table>),
52	/// A record type.
53	Record(Vec<Table>),
54	/// A geometry type.
55	Geometry(Vec<GeometryKind>),
56	/// An either type.
57	/// Can be any of the kinds in the vec.
58	Either(Vec<Kind>),
59	/// A set type.
60	Set(Box<Kind>, Option<u64>),
61	/// An array type.
62	Array(Box<Kind>, Option<u64>),
63	/// A function type.
64	/// The first option is the argument types, the second is the optional
65	/// return type.
66	Function(Option<Vec<Kind>>, Option<Box<Kind>>),
67	/// A range type.
68	Range,
69	/// A literal type.
70	/// The literal type is used to represent a type that can only be a single
71	/// value. For example, `"a"` is a literal type which can only ever be
72	/// `"a"`. This can be used in the `Kind::Either` type to represent an
73	/// enum.
74	Literal(KindLiteral),
75	/// A file type.
76	/// If the kind was specified without a bucket the vec will be empty.
77	/// So `<file>` is just `Kind::File(Vec::new())`
78	File(Vec<String>),
79}
80
81impl Kind {
82	/// Recursively flatten a kind into a vector of kinds.
83	pub fn flatten(self) -> Vec<Kind> {
84		match self {
85			Kind::Either(x) => x.into_iter().flat_map(|k| k.flatten()).collect(),
86			_ => vec![self],
87		}
88	}
89
90	/// Create an either kind from a vector of kinds.
91	/// If after dedeplication the vector is empty, return `Kind::None`.
92	/// If after dedeplication the vector has one element, return that element.
93	/// If after dedeplication the vector has multiple elements, return an `Either` kind with the
94	/// elements.
95	pub fn either(kinds: Vec<Kind>) -> Kind {
96		let mut seen = HashSet::new();
97		let mut kinds = kinds
98			.into_iter()
99			.flat_map(|k| k.flatten())
100			.filter(|k| seen.insert(k.clone()))
101			.collect::<Vec<_>>();
102		match kinds.len() {
103			0 => Kind::None,
104			1 => kinds.remove(0),
105			_ => Kind::Either(kinds),
106		}
107	}
108
109	/// Create an option kind from a kind.
110	pub fn option(kind: Kind) -> Kind {
111		Kind::either(vec![Kind::None, kind])
112	}
113}
114
115impl ToSql for Kind {
116	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
117		match self {
118			Kind::Any => f.push_str("any"),
119			Kind::None => f.push_str("none"),
120			Kind::Null => f.push_str("null"),
121			Kind::Bool => f.push_str("bool"),
122			Kind::Bytes => f.push_str("bytes"),
123			Kind::Datetime => f.push_str("datetime"),
124			Kind::Decimal => f.push_str("decimal"),
125			Kind::Duration => f.push_str("duration"),
126			Kind::Float => f.push_str("float"),
127			Kind::Int => f.push_str("int"),
128			Kind::Number => f.push_str("number"),
129			Kind::Object => f.push_str("object"),
130			Kind::String => f.push_str("string"),
131			Kind::Uuid => f.push_str("uuid"),
132			Kind::Regex => f.push_str("regex"),
133			Kind::Table(tables) => {
134				if tables.is_empty() {
135					f.push_str("table")
136				} else {
137					write_sql!(f, fmt, "table<{}>", format_seperated(tables, " | "));
138				}
139			}
140			Kind::Record(table) => {
141				if table.is_empty() {
142					f.push_str("record")
143				} else {
144					write_sql!(f, fmt, "record<{}>", format_seperated(table, " | "))
145				}
146			}
147			Kind::Geometry(kinds) => {
148				if kinds.is_empty() {
149					f.push_str("geometry")
150				} else {
151					write_sql!(f, fmt, "geometry<{}>", format_seperated(kinds, " | "))
152				}
153			}
154			Kind::Either(kinds) => write_sql!(f, fmt, "{}", format_seperated(kinds, " | ")),
155			Kind::Set(kind, max) => match max {
156				Some(max) => write_sql!(f, fmt, "set<{}, {}>", kind, max),
157				None => write_sql!(f, fmt, "set<{}>", kind),
158			},
159			Kind::Array(kind, max) => match max {
160				Some(max) => write_sql!(f, fmt, "array<{}, {}>", kind, max),
161				None => write_sql!(f, fmt, "array<{}>", kind),
162			},
163			Kind::Function(_, _) => f.push_str("function"),
164			Kind::Range => f.push_str("range"),
165			Kind::Literal(literal) => literal.fmt_sql(f, fmt),
166			Kind::File(bucket) => {
167				if bucket.is_empty() {
168					f.push_str("file")
169				} else {
170					write_sql!(f, fmt, "file<{}>", format_seperated(bucket, " | "))
171				}
172			}
173		}
174	}
175}
176
177impl Display for Kind {
178	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179		f.write_str(&self.to_sql())
180	}
181}