Skip to main content

surrealdb_types/kind/
literal.rs

1use std::collections::BTreeMap;
2use std::hash;
3
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6
7use crate::sql::fmt_sql_comma_separated;
8use crate::utils::escape::QuoteStr;
9use crate::{Duration, Kind, SqlFormat, ToSql, Value};
10
11/// Represents literal values in SurrealDB's type system
12///
13/// Literal types are used to represent specific values that can only be a single value.
14/// For example, a literal type `"a"` can only ever be the string `"a"`.
15/// This is commonly used in `Kind::Either` to represent enum-like types.
16#[derive(Clone, Debug, Serialize, Deserialize)]
17pub enum KindLiteral {
18	/// A string literal
19	String(String),
20	/// An integer literal
21	Integer(i64),
22	/// A floating-point literal
23	Float(f64),
24	/// A decimal literal
25	Decimal(Decimal),
26	/// A duration literal
27	Duration(Duration),
28	/// An array of kinds literal
29	Array(Vec<Kind>),
30	/// An object with string keys and kind values literal
31	Object(BTreeMap<String, Kind>),
32	/// A boolean literal
33	Bool(bool),
34}
35
36impl KindLiteral {
37	/// Check if a value matches this literal
38	pub fn matches(&self, value: &Value) -> bool {
39		match self {
40			KindLiteral::String(s) => {
41				if let Value::String(v) = value {
42					s == v
43				} else {
44					false
45				}
46			}
47			KindLiteral::Integer(i) => {
48				if let Value::Number(crate::Number::Int(v)) = value {
49					i == v
50				} else {
51					false
52				}
53			}
54			KindLiteral::Float(f) => {
55				if let Value::Number(crate::Number::Float(v)) = value {
56					f.to_bits() == v.to_bits()
57				} else {
58					false
59				}
60			}
61			KindLiteral::Decimal(d) => {
62				if let Value::Number(crate::Number::Decimal(v)) = value {
63					d == v
64				} else {
65					false
66				}
67			}
68			KindLiteral::Duration(d) => {
69				if let Value::Duration(v) = value {
70					d == v
71				} else {
72					false
73				}
74			}
75			KindLiteral::Array(kinds) => {
76				if let Value::Array(arr) = value {
77					if kinds.len() != arr.len() {
78						return false;
79					}
80					kinds.iter().zip(arr.iter()).all(|(kind, val)| val.is_kind(kind))
81				} else {
82					false
83				}
84			}
85			KindLiteral::Object(kinds) => {
86				if let Value::Object(obj) = value {
87					if kinds.len() != obj.len() {
88						return false;
89					}
90					kinds.iter().all(|(key, kind)| {
91						obj.get(key).map(|val| val.is_kind(kind)).unwrap_or(false)
92					})
93				} else {
94					false
95				}
96			}
97			KindLiteral::Bool(b) => {
98				if let Value::Bool(v) = value {
99					b == v
100				} else {
101					false
102				}
103			}
104		}
105	}
106}
107
108impl Eq for KindLiteral {}
109impl PartialEq for KindLiteral {
110	fn eq(&self, other: &Self) -> bool {
111		match self {
112			KindLiteral::String(strand) => {
113				if let KindLiteral::String(other) = other {
114					strand == other
115				} else {
116					false
117				}
118			}
119			KindLiteral::Integer(x) => {
120				if let KindLiteral::Integer(other) = other {
121					x == other
122				} else {
123					false
124				}
125			}
126			KindLiteral::Float(x) => {
127				if let KindLiteral::Float(other) = other {
128					x.to_bits() == other.to_bits()
129				} else {
130					false
131				}
132			}
133			KindLiteral::Decimal(decimal) => {
134				if let KindLiteral::Decimal(other) = other {
135					decimal == other
136				} else {
137					false
138				}
139			}
140			KindLiteral::Duration(duration) => {
141				if let KindLiteral::Duration(other) = other {
142					duration == other
143				} else {
144					false
145				}
146			}
147			KindLiteral::Array(kinds) => {
148				if let KindLiteral::Array(other) = other {
149					kinds == other
150				} else {
151					false
152				}
153			}
154			KindLiteral::Object(btree_map) => {
155				if let KindLiteral::Object(other) = other {
156					btree_map == other
157				} else {
158					false
159				}
160			}
161			KindLiteral::Bool(a) => {
162				if let KindLiteral::Bool(b) = other {
163					a == b
164				} else {
165					false
166				}
167			}
168		}
169	}
170}
171
172impl hash::Hash for KindLiteral {
173	fn hash<H: hash::Hasher>(&self, state: &mut H) {
174		std::mem::discriminant(self).hash(state);
175		match self {
176			KindLiteral::String(strand) => strand.hash(state),
177			KindLiteral::Integer(x) => x.hash(state),
178			KindLiteral::Float(x) => x.to_bits().hash(state),
179			KindLiteral::Decimal(decimal) => decimal.hash(state),
180			KindLiteral::Duration(duration) => duration.hash(state),
181			KindLiteral::Array(kinds) => kinds.hash(state),
182			KindLiteral::Object(btree_map) => btree_map.hash(state),
183			KindLiteral::Bool(x) => x.hash(state),
184		}
185	}
186}
187
188impl ToSql for KindLiteral {
189	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
190		match self {
191			KindLiteral::String(string) => QuoteStr(string).fmt_sql(f, fmt),
192			KindLiteral::Integer(x) => x.fmt_sql(f, fmt),
193			KindLiteral::Float(v) => {
194				if v.is_finite() {
195					// Add suffix to distinguish between int and float
196					v.fmt_sql(f, fmt);
197					f.push('f');
198				} else {
199					// Don't add suffix for NaN, inf, -inf
200					v.fmt_sql(f, fmt);
201				}
202			}
203			KindLiteral::Decimal(v) => {
204				v.fmt_sql(f, fmt);
205				f.push_str("dec");
206			}
207			KindLiteral::Duration(duration) => duration.fmt_sql_internal(f),
208			KindLiteral::Array(kinds) => {
209				f.push('[');
210				fmt_sql_comma_separated(kinds, f, fmt);
211				f.push(']');
212			}
213			KindLiteral::Object(btree_map) => {
214				f.push('{');
215				let items = btree_map
216					.iter()
217					.map(|(k, v)| format!("{}: {}", k.to_sql(), v.to_sql()))
218					.collect::<Vec<String>>();
219				fmt_sql_comma_separated(&items, f, fmt);
220				f.push('}');
221			}
222			KindLiteral::Bool(x) => x.fmt_sql(f, fmt),
223		}
224	}
225}