use super::Column;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ColumnFamily {
Integer,
Float,
Decimal,
Text,
Bool,
}
impl Column {
pub fn family(&self) -> Option<ColumnFamily> {
let name = base_name(self.original_type.as_deref()?);
let family = match name.as_str() {
"i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
| "u128" | "usize" | "int" | "integer" | "bigint" | "smallint" | "serial"
| "bigserial" => ColumnFamily::Integer,
"f32" | "f64" | "float" | "double" | "real" | "number" => ColumnFamily::Float,
"decimal" | "numeric" => ColumnFamily::Decimal,
"string" | "str" | "text" | "varchar" | "char" => ColumnFamily::Text,
"bool" | "boolean" => ColumnFamily::Bool,
_ => return None,
};
Some(family)
}
}
fn base_name(t: &str) -> String {
let t = t.trim().trim_start_matches('&').trim();
let (head, args) = match (t.find('<'), t.rfind('>')) {
(Some(open), Some(close)) if open < close => (&t[..open], Some(&t[open + 1..close])),
_ => (t, None),
};
let name = head.rsplit("::").next().unwrap_or(head);
if let Some(inner) = args
&& name == "Option"
{
return base_name(inner);
}
let name = name.split('(').next().unwrap_or(name);
name.trim().to_ascii_lowercase()
}
#[cfg(test)]
mod tests {
use std::any::type_name;
use super::*;
fn family(t: &str) -> Option<ColumnFamily> {
Column::new("c", t).family()
}
#[test]
fn rust_type_names_classify_like_their_short_names() {
assert_eq!(family(type_name::<String>()), Some(ColumnFamily::Text));
assert_eq!(family(type_name::<&str>()), Some(ColumnFamily::Text));
assert_eq!(
family(type_name::<Option<i64>>()),
Some(ColumnFamily::Integer)
);
assert_eq!(
family(type_name::<Option<String>>()),
Some(ColumnFamily::Text)
);
assert_eq!(family(type_name::<bool>()), Some(ColumnFamily::Bool));
assert_eq!(family(type_name::<f64>()), Some(ColumnFamily::Float));
assert_eq!(
family("rust_decimal::decimal::Decimal"),
Some(ColumnFamily::Decimal)
);
}
#[test]
fn spec_and_sql_type_names_classify_case_insensitively() {
assert_eq!(family("BIGINT"), Some(ColumnFamily::Integer));
assert_eq!(family("varchar(255)"), Some(ColumnFamily::Text));
assert_eq!(family("numeric(10, 2)"), Some(ColumnFamily::Decimal));
assert_eq!(family("Boolean"), Some(ColumnFamily::Bool));
assert_eq!(family("jsonb"), None);
assert_eq!(family(type_name::<Vec<u8>>()), None);
assert_eq!(Column::untyped("c").family(), None);
}
}