1use crate::span::Span;
4use crate::types::{CqlType, Order};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum ColumnRole {
10 PartitionKey,
12 ClusteringKey,
14 Regular,
16}
17
18#[derive(Clone, Debug, PartialEq)]
20#[non_exhaustive]
21pub struct Column {
22 pub name: String,
24 pub cql_type: CqlType,
26 pub role: ColumnRole,
28 pub is_static: bool,
30 pub masked: bool,
32 pub span: Span,
34}
35
36impl Column {
37 pub fn is_key(&self) -> bool {
39 self.role != ColumnRole::Regular
40 }
41
42 pub fn nullable(&self) -> bool {
44 !self.is_key()
45 }
46}
47
48#[derive(Clone, Debug, PartialEq)]
50#[non_exhaustive]
51pub struct ClusteringColumn {
52 pub name: String,
54 pub order: Order,
56}
57
58#[derive(Clone, Debug, PartialEq)]
60#[non_exhaustive]
61pub struct Table {
62 pub keyspace: Option<String>,
64 pub name: String,
66 pub columns: Vec<Column>,
68 pub partition_key: Vec<String>,
70 pub clustering: Vec<ClusteringColumn>,
72 pub base_table: Option<String>,
74 pub indexes: Vec<Index>,
76 pub span: Span,
78}
79
80#[derive(Clone, Debug, PartialEq, Eq)]
82#[non_exhaustive]
83pub enum IndexClass {
84 Legacy,
86 Sai,
88 Sasi,
90 Custom(String),
92}
93
94impl IndexClass {
95 pub fn from_using(class: Option<&str>) -> IndexClass {
97 match class.map(str::to_ascii_lowercase).as_deref() {
98 None => IndexClass::Legacy,
99 Some(c) if c == "sai" || c.contains("storageattachedindex") => IndexClass::Sai,
100 Some(c) if c.contains("sasi") => IndexClass::Sasi,
101 Some(_) => IndexClass::Custom(class.unwrap_or_default().to_string()),
102 }
103 }
104}
105
106#[derive(Clone, Debug, PartialEq)]
108#[non_exhaustive]
109pub struct Index {
110 pub name: Option<String>,
112 pub column: String,
114 pub kind: crate::ast::IndexKind,
116 pub class: IndexClass,
118}
119
120#[derive(Clone, Debug, PartialEq)]
122#[non_exhaustive]
123pub struct Function {
124 pub name: String,
126 pub args: Vec<CqlType>,
128 pub returns: CqlType,
130 pub aggregate: bool,
132}
133
134impl Table {
135 pub fn index_for(&self, column: &str) -> Option<&Index> {
137 self.indexes.iter().find(|i| i.column == column)
138 }
139
140 pub fn column(&self, name: &str) -> Option<&Column> {
142 self.columns.iter().find(|c| c.name == name)
143 }
144
145 pub fn is_counter_table(&self) -> bool {
147 self.columns
148 .iter()
149 .any(|c| matches!(c.cql_type, CqlType::Counter))
150 }
151
152 pub fn qualified_name(&self) -> String {
154 match &self.keyspace {
155 Some(ks) => format!("{ks}.{}", self.name),
156 None => self.name.clone(),
157 }
158 }
159}
160
161#[derive(Clone, Debug, PartialEq)]
163#[non_exhaustive]
164pub struct Udt {
165 pub keyspace: Option<String>,
167 pub name: String,
169 pub fields: Vec<(String, CqlType)>,
171 pub span: Span,
173}
174
175#[derive(Clone, Debug, Default, PartialEq)]
177#[non_exhaustive]
178pub struct Schema {
179 pub tables: Vec<Table>,
181 pub udts: Vec<Udt>,
183 pub functions: Vec<Function>,
185}
186
187impl Schema {
188 pub fn function(&self, name: &str) -> Option<&Function> {
190 self.functions.iter().find(|f| f.name == name)
191 }
192
193 pub fn udt(&self, name: &str) -> Option<&Udt> {
195 self.udts.iter().find(|u| u.name == name)
196 }
197}
198
199#[derive(Debug)]
201#[non_exhaustive]
202pub enum Lookup<'a> {
203 Found(&'a Table),
205 NotFound,
207 Ambiguous(Vec<&'a Table>),
209}
210
211impl Schema {
212 pub fn lookup_table(&self, keyspace: Option<&str>, name: &str) -> Lookup<'_> {
215 let matches: Vec<&Table> = self
216 .tables
217 .iter()
218 .filter(|t| {
219 t.name == name && keyspace.is_none_or(|ks| t.keyspace.as_deref() == Some(ks))
220 })
221 .collect();
222 match matches.len() {
223 0 => Lookup::NotFound,
224 1 => Lookup::Found(matches[0]),
225 _ => Lookup::Ambiguous(matches),
226 }
227 }
228}