use crate::error::SqlError;
use crate::types::SqlExpr;
#[derive(Debug, Clone)]
pub(super) enum DefaultKind {
Generator(Generator),
Literal(nodedb_types::Value),
Expr(SqlExpr),
}
#[derive(Debug, Clone, Copy)]
pub(super) enum Generator {
UuidV7,
UuidV4,
Ulid,
Cuid2,
Cuid2Len(usize),
Nanoid,
NanoidLen(usize),
Now,
}
impl Generator {
pub(super) fn generate(self) -> crate::Result<nodedb_types::Value> {
let value = match self {
Self::UuidV7 => nodedb_types::Value::String(nodedb_types::id_gen::uuid_v7()),
Self::UuidV4 => nodedb_types::Value::String(nodedb_types::id_gen::uuid_v4()),
Self::Ulid => nodedb_types::Value::String(nodedb_types::id_gen::ulid()),
Self::Cuid2 => nodedb_types::Value::String(nodedb_types::id_gen::cuid2()),
Self::Cuid2Len(len) => nodedb_types::Value::String(cuid2_with_length(len)?),
Self::Nanoid => nodedb_types::Value::String(nodedb_types::id_gen::nanoid()),
Self::NanoidLen(len) => {
nodedb_types::Value::String(nodedb_types::id_gen::nanoid_with_length(len))
}
Self::Now => nodedb_types::Value::String(now_rfc3339()),
};
Ok(value)
}
}
pub(super) fn cuid2_with_length(len: usize) -> crate::Result<String> {
nodedb_types::id_gen::cuid2_with_length(len).map_err(|e| SqlError::Parse {
detail: format!("CUID2({len}) default expression is invalid: {e}"),
})
}
fn now_rfc3339() -> String {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default();
chrono::DateTime::from_timestamp_millis(now.as_millis() as i64)
.map(|dt| dt.to_rfc3339())
.unwrap_or_else(|| now.as_millis().to_string())
}
pub(super) fn keyword_generator(upper: &str) -> Option<Generator> {
let generator = match upper {
"UUID_V7" | "UUIDV7" | "GEN_UUID_V7()" | "UUID_V7()" => Generator::UuidV7,
"UUID_V4" | "UUIDV4" | "UUID" | "GEN_UUID_V4()" | "UUID_V4()" => Generator::UuidV4,
"ULID" | "GEN_ULID()" | "ULID()" => Generator::Ulid,
"CUID2" | "CUID2()" => Generator::Cuid2,
"NANOID" | "NANOID()" => Generator::Nanoid,
"NOW()" => Generator::Now,
_ => return None,
};
Some(generator)
}
pub(super) fn parametric_or_literal(expr: &str, upper: &str) -> crate::Result<Option<DefaultKind>> {
if upper.starts_with("NANOID(") && upper.ends_with(')') {
let len_str = &upper[7..upper.len() - 1];
if let Ok(len) = len_str.parse::<usize>() {
return Ok(Some(DefaultKind::Generator(Generator::NanoidLen(len))));
}
}
if upper.starts_with("CUID2(") && upper.ends_with(')') {
let len_str = &upper[6..upper.len() - 1];
if let Ok(len) = len_str.parse::<usize>() {
cuid2_with_length(len)?;
return Ok(Some(DefaultKind::Generator(Generator::Cuid2Len(len))));
}
}
if let Ok(i) = expr.trim().parse::<i64>() {
return Ok(Some(DefaultKind::Literal(nodedb_types::Value::Integer(i))));
}
if let Ok(f) = expr.trim().parse::<f64>() {
return Ok(Some(DefaultKind::Literal(nodedb_types::Value::Float(f))));
}
let trimmed = expr.trim();
if trimmed.len() >= 2
&& ((trimmed.starts_with('\'') && trimmed.ends_with('\''))
|| (trimmed.starts_with('"') && trimmed.ends_with('"')))
{
return Ok(Some(DefaultKind::Literal(nodedb_types::Value::String(
trimmed[1..trimmed.len() - 1].to_string(),
))));
}
Ok(None)
}