use crate::span::Span;
use crate::types::{CqlType, Order};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ColumnRole {
PartitionKey,
ClusteringKey,
Regular,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Column {
pub name: String,
pub cql_type: CqlType,
pub role: ColumnRole,
pub is_static: bool,
pub masked: bool,
pub span: Span,
}
impl Column {
pub fn is_key(&self) -> bool {
self.role != ColumnRole::Regular
}
pub fn nullable(&self) -> bool {
!self.is_key()
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct ClusteringColumn {
pub name: String,
pub order: Order,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Table {
pub keyspace: Option<String>,
pub name: String,
pub columns: Vec<Column>,
pub partition_key: Vec<String>,
pub clustering: Vec<ClusteringColumn>,
pub base_table: Option<String>,
pub indexes: Vec<Index>,
pub span: Span,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum IndexClass {
Legacy,
Sai,
Sasi,
Custom(String),
}
impl IndexClass {
pub fn from_using(class: Option<&str>) -> IndexClass {
match class.map(str::to_ascii_lowercase).as_deref() {
None => IndexClass::Legacy,
Some(c) if c == "sai" || c.contains("storageattachedindex") => IndexClass::Sai,
Some(c) if c.contains("sasi") => IndexClass::Sasi,
Some(_) => IndexClass::Custom(class.unwrap_or_default().to_string()),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Index {
pub name: Option<String>,
pub column: String,
pub kind: crate::ast::IndexKind,
pub class: IndexClass,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Function {
pub name: String,
pub args: Vec<CqlType>,
pub returns: CqlType,
pub aggregate: bool,
}
impl Table {
pub fn index_for(&self, column: &str) -> Option<&Index> {
self.indexes.iter().find(|i| i.column == column)
}
pub fn column(&self, name: &str) -> Option<&Column> {
self.columns.iter().find(|c| c.name == name)
}
pub fn is_counter_table(&self) -> bool {
self.columns
.iter()
.any(|c| matches!(c.cql_type, CqlType::Counter))
}
pub fn qualified_name(&self) -> String {
match &self.keyspace {
Some(ks) => format!("{ks}.{}", self.name),
None => self.name.clone(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Udt {
pub keyspace: Option<String>,
pub name: String,
pub fields: Vec<(String, CqlType)>,
pub span: Span,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[non_exhaustive]
pub struct Schema {
pub tables: Vec<Table>,
pub udts: Vec<Udt>,
pub functions: Vec<Function>,
}
impl Schema {
pub fn function(&self, name: &str) -> Option<&Function> {
self.functions.iter().find(|f| f.name == name)
}
pub fn udt(&self, name: &str) -> Option<&Udt> {
self.udts.iter().find(|u| u.name == name)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Lookup<'a> {
Found(&'a Table),
NotFound,
Ambiguous(Vec<&'a Table>),
}
impl Schema {
pub fn lookup_table(&self, keyspace: Option<&str>, name: &str) -> Lookup<'_> {
let matches: Vec<&Table> = self
.tables
.iter()
.filter(|t| {
t.name == name && keyspace.is_none_or(|ks| t.keyspace.as_deref() == Some(ks))
})
.collect();
match matches.len() {
0 => Lookup::NotFound,
1 => Lookup::Found(matches[0]),
_ => Lookup::Ambiguous(matches),
}
}
}