Skip to main content

tank_core/
column.rs

1use crate::{
2    DefaultValueType, DynQuery, Expression, ExpressionVisitor, OpPrecedence, SqlWriter, TableRef,
3    Value, writer::Context,
4};
5use proc_macro2::TokenStream;
6use quote::{ToTokens, TokenStreamExt, quote};
7use std::{borrow::Cow, collections::BTreeMap, hash::Hash};
8
9/// Trait exposing column definition and reference.
10pub trait ColumnTrait {
11    /// Returns the column definition, including metadata like type and constraints.
12    fn column_def(&self) -> &ColumnDef;
13    /// Returns the reference identifying the column (name, table, schema).
14    fn column_ref(&self) -> &ColumnRef;
15}
16
17/// Reference to a column.
18#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
19pub struct ColumnRef {
20    /// Name of the column.
21    pub name: Cow<'static, str>,
22    /// Name of the table.
23    pub table: Cow<'static, str>,
24    /// Name of the schema.
25    pub schema: Cow<'static, str>,
26}
27
28impl ColumnRef {
29    /// Create a column reference from just a name.
30    pub fn new(name: Cow<'static, str>) -> Self {
31        Self {
32            name,
33            ..Default::default()
34        }
35    }
36    /// Get the table reference for this column.
37    pub fn table(&self) -> TableRef {
38        TableRef {
39            name: self.table.clone(),
40            schema: self.schema.clone(),
41            ..Default::default()
42        }
43    }
44}
45
46/// Primary key participation.
47#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
48pub enum PrimaryKeyType {
49    /// Full primary key.
50    PrimaryKey,
51    /// Part of a composite key.
52    PartOfPrimaryKey,
53    /// Not in primary key.
54    #[default]
55    None,
56}
57
58impl ToTokens for PrimaryKeyType {
59    fn to_tokens(&self, tokens: &mut TokenStream) {
60        use PrimaryKeyType::*;
61        tokens.append_all(match self {
62            PrimaryKey => quote!(::tank::PrimaryKeyType::PrimaryKey),
63            PartOfPrimaryKey => quote!(::tank::PrimaryKeyType::PartOfPrimaryKey),
64            None => quote!(::tank::PrimaryKeyType::None),
65        });
66    }
67}
68
69/// Referential action for foreign key updates or deletes.
70#[derive(Default, Debug, PartialEq, Eq)]
71pub enum Action {
72    /// No action.
73    #[default]
74    NoAction,
75    /// Reject operation.
76    Restrict,
77    /// Propagate change.
78    Cascade,
79    /// Set to NULL.
80    SetNull,
81    /// Set to DEFAULT.
82    SetDefault,
83}
84
85impl ToTokens for Action {
86    fn to_tokens(&self, tokens: &mut TokenStream) {
87        tokens.append_all(match self {
88            Action::NoAction => quote! { ::tank::Action::NoAction },
89            Action::Restrict => quote! { ::tank::Action::Restrict },
90            Action::Cascade => quote! { ::tank::Action::Cascade },
91            Action::SetNull => quote! { ::tank::Action::SetNull },
92            Action::SetDefault => quote! { ::tank::Action::SetDefault },
93        });
94    }
95}
96
97/// Column specification.
98#[derive(Default, Debug)]
99pub struct ColumnDef {
100    /// Column identity.
101    pub column_ref: ColumnRef,
102    /// Explicit SQL types.
103    pub column_type: BTreeMap<&'static str, &'static str>,
104    /// Type descriptor.
105    pub value: Value,
106    /// Is nullable.
107    pub nullable: bool,
108    /// Default expression.
109    pub default: DefaultValueType,
110    /// Primary key role.
111    pub primary_key: PrimaryKeyType,
112    /// Clustering key (relevant for ScyllaDB/Cassandra).
113    pub clustering_key: bool,
114    /// Single-column unique constraint.
115    pub unique: bool,
116    /// Foreign key target.
117    pub references: Option<ColumnRef>,
118    /// On delete action.
119    pub on_delete: Option<Action>,
120    /// On update action.
121    pub on_update: Option<Action>,
122    /// Comment.
123    pub comment: &'static str,
124}
125
126impl ColumnDef {
127    /// Column name.
128    pub fn name(&self) -> &str {
129        &self.column_ref.name
130    }
131    /// Table name.
132    pub fn table(&self) -> &str {
133        &self.column_ref.table
134    }
135    /// Schema name.
136    pub fn schema(&self) -> &str {
137        &self.column_ref.schema
138    }
139}
140
141impl<'a> From<&'a ColumnDef> for &'a ColumnRef {
142    fn from(value: &'a ColumnDef) -> Self {
143        &value.column_ref
144    }
145}
146
147impl OpPrecedence for ColumnRef {
148    fn precedence(&self, _writer: &dyn SqlWriter) -> i32 {
149        1_000_000
150    }
151}
152
153impl Expression for ColumnRef {
154    fn write_query(&self, writer: &dyn SqlWriter, context: &mut Context, out: &mut DynQuery) {
155        writer.write_column_ref(context, out, self);
156    }
157    fn accept_visitor(
158        &self,
159        matcher: &mut dyn ExpressionVisitor,
160        writer: &dyn SqlWriter,
161        context: &mut Context,
162        out: &mut DynQuery,
163    ) -> bool {
164        matcher.visit_column(writer, context, out, self)
165    }
166}
167
168impl OpPrecedence for ColumnDef {
169    fn precedence(&self, _writer: &dyn SqlWriter) -> i32 {
170        1_000_000
171    }
172}
173
174impl Expression for ColumnDef {
175    fn write_query(&self, writer: &dyn SqlWriter, context: &mut Context, out: &mut DynQuery) {
176        writer.write_column_ref(context, out, &self.column_ref);
177    }
178
179    fn accept_visitor(
180        &self,
181        matcher: &mut dyn ExpressionVisitor,
182        writer: &dyn SqlWriter,
183        context: &mut Context,
184        out: &mut DynQuery,
185    ) -> bool {
186        matcher.visit_column(writer, context, out, &self.column_ref)
187    }
188}
189
190impl PartialEq for ColumnDef {
191    fn eq(&self, other: &Self) -> bool {
192        self.column_ref == other.column_ref
193    }
194}
195
196impl Eq for ColumnDef {}
197
198impl Hash for ColumnDef {
199    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
200        self.column_ref.hash(state)
201    }
202}