mod geometry;
mod literal;
use std::collections::HashSet;
use std::fmt::Display;
pub use geometry::*;
pub use literal::*;
use serde::{Deserialize, Serialize};
use surrealdb_types_derive::write_sql;
use crate::utils::display::format_seperated;
use crate::{self as surrealdb_types, SqlFormat, Table, ToSql};
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum Kind {
#[default]
Any,
None,
Null,
Bool,
Bytes,
Datetime,
Decimal,
Duration,
Float,
Int,
Number,
Object,
String,
Uuid,
Regex,
Table(Vec<Table>),
Record(Vec<Table>),
Geometry(Vec<GeometryKind>),
Either(Vec<Kind>),
Set(Box<Kind>, Option<u64>),
Array(Box<Kind>, Option<u64>),
Function(Option<Vec<Kind>>, Option<Box<Kind>>),
Range,
Literal(KindLiteral),
File(Vec<String>),
}
impl Kind {
pub fn flatten(self) -> Vec<Kind> {
match self {
Kind::Either(x) => x.into_iter().flat_map(|k| k.flatten()).collect(),
_ => vec![self],
}
}
pub fn either(kinds: Vec<Kind>) -> Kind {
let mut seen = HashSet::new();
let mut kinds = kinds
.into_iter()
.flat_map(|k| k.flatten())
.filter(|k| seen.insert(k.clone()))
.collect::<Vec<_>>();
match kinds.len() {
0 => Kind::None,
1 => kinds.remove(0),
_ => Kind::Either(kinds),
}
}
pub fn option(kind: Kind) -> Kind {
Kind::either(vec![Kind::None, kind])
}
}
impl ToSql for Kind {
fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
match self {
Kind::Any => f.push_str("any"),
Kind::None => f.push_str("none"),
Kind::Null => f.push_str("null"),
Kind::Bool => f.push_str("bool"),
Kind::Bytes => f.push_str("bytes"),
Kind::Datetime => f.push_str("datetime"),
Kind::Decimal => f.push_str("decimal"),
Kind::Duration => f.push_str("duration"),
Kind::Float => f.push_str("float"),
Kind::Int => f.push_str("int"),
Kind::Number => f.push_str("number"),
Kind::Object => f.push_str("object"),
Kind::String => f.push_str("string"),
Kind::Uuid => f.push_str("uuid"),
Kind::Regex => f.push_str("regex"),
Kind::Table(tables) => {
if tables.is_empty() {
f.push_str("table")
} else {
write_sql!(f, fmt, "table<{}>", format_seperated(tables, " | "));
}
}
Kind::Record(table) => {
if table.is_empty() {
f.push_str("record")
} else {
write_sql!(f, fmt, "record<{}>", format_seperated(table, " | "))
}
}
Kind::Geometry(kinds) => {
if kinds.is_empty() {
f.push_str("geometry")
} else {
write_sql!(f, fmt, "geometry<{}>", format_seperated(kinds, " | "))
}
}
Kind::Either(kinds) => write_sql!(f, fmt, "{}", format_seperated(kinds, " | ")),
Kind::Set(kind, max) => match max {
Some(max) => write_sql!(f, fmt, "set<{}, {}>", kind, max),
None => write_sql!(f, fmt, "set<{}>", kind),
},
Kind::Array(kind, max) => match max {
Some(max) => write_sql!(f, fmt, "array<{}, {}>", kind, max),
None => write_sql!(f, fmt, "array<{}>", kind),
},
Kind::Function(_, _) => f.push_str("function"),
Kind::Range => f.push_str("range"),
Kind::Literal(literal) => literal.fmt_sql(f, fmt),
Kind::File(bucket) => {
if bucket.is_empty() {
f.push_str("file")
} else {
write_sql!(f, fmt, "file<{}>", format_seperated(bucket, " | "))
}
}
}
}
}
impl Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_sql())
}
}