Skip to main content

cqlite_core/cql/
ast.rs

1//! Abstract Syntax Tree definitions for CQL statements
2//!
3//! This module defines the AST node types that represent parsed CQL statements.
4//! The AST is designed to be parser-agnostic and provides a unified representation
5//! that can be generated by different parser backends (nom, ANTLR, etc.).
6
7use crate::schema::CqlType;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// Top-level CQL statement
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub enum CqlStatement {
14    /// SELECT statement
15    Select(CqlSelect),
16    /// INSERT statement
17    Insert(CqlInsert),
18    /// UPDATE statement
19    Update(CqlUpdate),
20    /// DELETE statement
21    Delete(CqlDelete),
22    /// CREATE TABLE statement
23    CreateTable(CqlCreateTable),
24    /// DROP TABLE statement
25    DropTable(CqlDropTable),
26    /// CREATE INDEX statement
27    CreateIndex(CqlCreateIndex),
28    /// ALTER TABLE statement
29    AlterTable(CqlAlterTable),
30    /// CREATE TYPE (UDT) statement
31    CreateType(CqlCreateType),
32    /// DROP TYPE statement
33    DropType(CqlDropType),
34    /// USE statement (keyspace selection)
35    Use(CqlUse),
36    /// TRUNCATE statement
37    Truncate(CqlTruncate),
38    /// BATCH statement
39    Batch(CqlBatch),
40}
41
42/// SELECT statement AST
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct CqlSelect {
45    /// DISTINCT modifier
46    pub distinct: bool,
47    /// Selected columns/expressions
48    pub select_list: Vec<CqlSelectItem>,
49    /// FROM clause
50    pub from: CqlTable,
51    /// WHERE clause (optional)
52    pub where_clause: Option<CqlExpression>,
53    /// ORDER BY clause (optional)
54    pub order_by: Option<Vec<CqlOrderBy>>,
55    /// LIMIT clause (optional)
56    pub limit: Option<u64>,
57    /// ALLOW FILTERING modifier
58    pub allow_filtering: bool,
59}
60
61/// Item in the SELECT list
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63pub enum CqlSelectItem {
64    /// Wildcard (*)
65    Wildcard,
66    /// Expression with optional alias
67    Expression {
68        expression: CqlExpression,
69        alias: Option<CqlIdentifier>,
70    },
71    /// Function call
72    Function {
73        name: CqlIdentifier,
74        args: Vec<CqlExpression>,
75        alias: Option<CqlIdentifier>,
76    },
77}
78
79/// INSERT statement AST
80#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
81pub struct CqlInsert {
82    /// Target table
83    pub table: CqlTable,
84    /// Column names
85    pub columns: Vec<CqlIdentifier>,
86    /// Values to insert
87    pub values: CqlInsertValues,
88    /// IF NOT EXISTS modifier
89    pub if_not_exists: bool,
90    /// USING clause (TTL, TIMESTAMP)
91    pub using: Option<CqlUsing>,
92}
93
94/// INSERT values
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub enum CqlInsertValues {
97    /// VALUES clause with literal values
98    Values(Vec<CqlExpression>),
99    /// JSON values
100    Json(String),
101}
102
103/// UPDATE statement AST
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct CqlUpdate {
106    /// Target table
107    pub table: CqlTable,
108    /// USING clause (TTL, TIMESTAMP)
109    pub using: Option<CqlUsing>,
110    /// SET assignments
111    pub assignments: Vec<CqlAssignment>,
112    /// WHERE clause
113    pub where_clause: CqlExpression,
114    /// IF condition (optional)
115    pub if_condition: Option<CqlExpression>,
116}
117
118/// Assignment in UPDATE statement
119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
120pub struct CqlAssignment {
121    /// Target column
122    pub column: CqlIdentifier,
123    /// Assignment operator
124    pub operator: CqlAssignmentOperator,
125    /// Value expression
126    pub value: CqlExpression,
127}
128
129/// Assignment operators
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131pub enum CqlAssignmentOperator {
132    /// Simple assignment (=)
133    Assign,
134    /// Addition assignment (+=)
135    AddAssign,
136    /// Subtraction assignment (-=)
137    SubAssign,
138    /// List append (+=)
139    ListAppend,
140    /// List prepend (= value +)
141    ListPrepend,
142    /// Set add (+=)
143    SetAdd,
144    /// Set remove (-=)
145    SetRemove,
146    /// Map update ([key] = value)
147    MapUpdate(CqlExpression),
148}
149
150/// DELETE statement AST
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub struct CqlDelete {
153    /// Columns to delete (optional - if empty, delete entire row)
154    pub columns: Vec<CqlIdentifier>,
155    /// Target table
156    pub table: CqlTable,
157    /// USING clause (TIMESTAMP)
158    pub using: Option<CqlUsing>,
159    /// WHERE clause
160    pub where_clause: CqlExpression,
161    /// IF condition (optional)
162    pub if_condition: Option<CqlExpression>,
163}
164
165/// CREATE TABLE statement AST
166#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
167pub struct CqlCreateTable {
168    /// IF NOT EXISTS modifier
169    pub if_not_exists: bool,
170    /// Table name
171    pub table: CqlTable,
172    /// Column definitions
173    pub columns: Vec<CqlColumnDef>,
174    /// Primary key definition
175    pub primary_key: CqlPrimaryKey,
176    /// Table options (WITH clause)
177    pub options: CqlTableOptions,
178}
179
180/// Column definition
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct CqlColumnDef {
183    /// Column name
184    pub name: CqlIdentifier,
185    /// Data type
186    pub data_type: CqlDataType,
187    /// STATIC modifier
188    pub is_static: bool,
189}
190
191/// Primary key definition
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
193pub struct CqlPrimaryKey {
194    /// Partition key columns
195    pub partition_key: Vec<CqlIdentifier>,
196    /// Clustering key columns (optional)
197    pub clustering_key: Vec<CqlIdentifier>,
198}
199
200/// DROP TABLE statement AST
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202pub struct CqlDropTable {
203    /// IF EXISTS modifier
204    pub if_exists: bool,
205    /// Table name
206    pub table: CqlTable,
207}
208
209/// CREATE INDEX statement AST
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub struct CqlCreateIndex {
212    /// IF NOT EXISTS modifier
213    pub if_not_exists: bool,
214    /// Index name (optional)
215    pub name: Option<CqlIdentifier>,
216    /// Target table
217    pub table: CqlTable,
218    /// Indexed columns/expressions
219    pub columns: Vec<CqlIndexColumn>,
220    /// USING clause (index type)
221    pub using: Option<String>,
222    /// Index options
223    pub options: HashMap<String, String>,
224}
225
226/// Index column specification
227#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
228pub enum CqlIndexColumn {
229    /// Simple column reference
230    Column(CqlIdentifier),
231    /// KEYS() function for map columns
232    Keys(CqlIdentifier),
233    /// VALUES() function for map columns
234    Values(CqlIdentifier),
235    /// ENTRIES() function for map columns
236    Entries(CqlIdentifier),
237    /// FULL() function for collection columns
238    Full(CqlIdentifier),
239}
240
241/// ALTER TABLE statement AST
242#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
243pub struct CqlAlterTable {
244    /// Target table
245    pub table: CqlTable,
246    /// Alteration operation
247    pub operation: CqlAlterTableOp,
248}
249
250/// ALTER TABLE operations
251#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
252pub enum CqlAlterTableOp {
253    /// ADD column
254    AddColumn(CqlColumnDef),
255    /// DROP column
256    DropColumn(CqlIdentifier),
257    /// ALTER column type
258    AlterColumn {
259        column: CqlIdentifier,
260        new_type: CqlDataType,
261    },
262    /// RENAME column
263    RenameColumn {
264        old_name: CqlIdentifier,
265        new_name: CqlIdentifier,
266    },
267    /// WITH options
268    WithOptions(CqlTableOptions),
269}
270
271/// CREATE TYPE (UDT) statement AST
272#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
273pub struct CqlCreateType {
274    /// IF NOT EXISTS modifier
275    pub if_not_exists: bool,
276    /// Type name
277    pub name: CqlIdentifier,
278    /// Field definitions
279    pub fields: Vec<CqlUdtField>,
280}
281
282/// UDT field definition
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284pub struct CqlUdtField {
285    /// Field name
286    pub name: CqlIdentifier,
287    /// Field data type
288    pub data_type: CqlDataType,
289}
290
291/// DROP TYPE statement AST
292#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
293pub struct CqlDropType {
294    /// IF EXISTS modifier
295    pub if_exists: bool,
296    /// Type name
297    pub name: CqlIdentifier,
298}
299
300/// USE statement AST
301#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
302pub struct CqlUse {
303    /// Keyspace name
304    pub keyspace: CqlIdentifier,
305}
306
307/// TRUNCATE statement AST
308#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309pub struct CqlTruncate {
310    /// Target table
311    pub table: CqlTable,
312}
313
314/// BATCH statement AST.
315///
316/// The parser accepts multi-table batches (each statement may reference a different table),
317/// but the write engine processes each statement independently against the provided schema.
318/// Cross-table atomicity is not guaranteed by the local write engine.
319#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
320pub struct CqlBatch {
321    /// Batch type
322    pub batch_type: CqlBatchType,
323    /// USING clause (TIMESTAMP)
324    pub using: Option<CqlUsing>,
325    /// Statements in the batch (max 65535 entries, enforced by parser)
326    pub statements: Vec<CqlBatchStatement>,
327}
328
329/// Batch types
330#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
331pub enum CqlBatchType {
332    /// LOGGED batch (default)
333    Logged,
334    /// UNLOGGED batch
335    Unlogged,
336    /// COUNTER batch
337    Counter,
338}
339
340/// Statement allowed in a batch
341#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
342pub enum CqlBatchStatement {
343    /// INSERT statement
344    Insert(CqlInsert),
345    /// UPDATE statement
346    Update(CqlUpdate),
347    /// DELETE statement
348    Delete(CqlDelete),
349}
350
351/// CQL expression AST
352#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
353pub enum CqlExpression {
354    /// Literal value
355    Literal(CqlLiteral),
356    /// Column reference
357    Column(CqlIdentifier),
358    /// Parameter placeholder (?)
359    Parameter(u32),
360    /// Named parameter (:name)
361    NamedParameter(String),
362    /// Binary operation (AND, OR, =, !=, <, >, etc.)
363    Binary {
364        left: Box<CqlExpression>,
365        operator: CqlBinaryOperator,
366        right: Box<CqlExpression>,
367    },
368    /// Unary operation (NOT, -)
369    Unary {
370        operator: CqlUnaryOperator,
371        operand: Box<CqlExpression>,
372    },
373    /// Function call
374    Function {
375        name: CqlIdentifier,
376        args: Vec<CqlExpression>,
377    },
378    /// IN clause
379    In {
380        expression: Box<CqlExpression>,
381        values: Vec<CqlExpression>,
382    },
383    /// CONTAINS clause
384    Contains {
385        column: CqlIdentifier,
386        value: Box<CqlExpression>,
387    },
388    /// CONTAINS KEY clause
389    ContainsKey {
390        column: CqlIdentifier,
391        key: Box<CqlExpression>,
392    },
393    /// Collection access [index] or [key]
394    CollectionAccess {
395        collection: Box<CqlExpression>,
396        index: Box<CqlExpression>,
397    },
398    /// UDT field access (udt.field)
399    FieldAccess {
400        object: Box<CqlExpression>,
401        field: CqlIdentifier,
402    },
403    /// CASE expression
404    Case {
405        when_clauses: Vec<CqlWhenClause>,
406        else_clause: Option<Box<CqlExpression>>,
407    },
408    /// Type cast (CAST)
409    Cast {
410        expression: Box<CqlExpression>,
411        target_type: CqlDataType,
412    },
413}
414
415/// WHEN clause in CASE expression
416#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
417pub struct CqlWhenClause {
418    /// Condition
419    pub condition: CqlExpression,
420    /// Result if condition is true
421    pub result: CqlExpression,
422}
423
424/// Binary operators
425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
426pub enum CqlBinaryOperator {
427    // Logical
428    And,
429    Or,
430
431    // Comparison
432    Eq,
433    Ne,
434    Lt,
435    Le,
436    Gt,
437    Ge,
438
439    // Arithmetic
440    Add,
441    Sub,
442    Mul,
443    Div,
444    Mod,
445
446    // String
447    Like,
448}
449
450/// Unary operators
451#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
452pub enum CqlUnaryOperator {
453    /// Logical NOT
454    Not,
455    /// Arithmetic negation
456    Minus,
457    /// Arithmetic positive (unary +)
458    Plus,
459}
460
461/// CQL literal values
462#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
463pub enum CqlLiteral {
464    /// NULL value
465    Null,
466    /// Boolean value
467    Boolean(bool),
468    /// Integer value
469    Integer(i64),
470    /// Float value
471    Float(f64),
472    /// String value
473    String(String),
474    /// UUID value
475    Uuid(String),
476    /// Blob value (hex string)
477    Blob(String),
478    /// Collection literal
479    Collection(CqlCollectionLiteral),
480    /// UDT literal
481    Udt(CqlUdtLiteral),
482    /// Tuple literal
483    Tuple(Vec<CqlLiteral>),
484}
485
486/// Collection literal
487#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
488pub enum CqlCollectionLiteral {
489    /// List literal [item1, item2, ...]
490    List(Vec<CqlLiteral>),
491    /// Set literal {item1, item2, ...}
492    Set(Vec<CqlLiteral>),
493    /// Map literal {key1: value1, key2: value2, ...}
494    Map(Vec<(CqlLiteral, CqlLiteral)>),
495}
496
497/// UDT literal
498#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
499pub struct CqlUdtLiteral {
500    /// UDT type name (optional)
501    pub type_name: Option<CqlIdentifier>,
502    /// Field values
503    pub fields: Vec<(CqlIdentifier, CqlLiteral)>,
504}
505
506/// CQL data type AST
507#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
508pub enum CqlDataType {
509    /// Primitive types
510    Boolean,
511    TinyInt,
512    SmallInt,
513    Int,
514    BigInt,
515    Varint,
516    Decimal,
517    Float,
518    Double,
519    Text,
520    Ascii,
521    Varchar,
522    Blob,
523    Timestamp,
524    Date,
525    Time,
526    Uuid,
527    TimeUuid,
528    Inet,
529    Duration,
530    Counter,
531
532    /// Collection types
533    List(Box<CqlDataType>),
534    Set(Box<CqlDataType>),
535    Map(Box<CqlDataType>, Box<CqlDataType>),
536
537    /// Complex types
538    Tuple(Vec<CqlDataType>),
539    Udt(CqlIdentifier),
540    Frozen(Box<CqlDataType>),
541
542    /// Custom type
543    Custom(String),
544}
545
546/// CQL identifier
547#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
548pub struct CqlIdentifier {
549    /// The identifier name
550    pub name: String,
551    /// Whether the identifier is quoted
552    pub quoted: bool,
553}
554
555/// Table reference
556#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
557pub struct CqlTable {
558    /// Keyspace name (optional)
559    pub keyspace: Option<CqlIdentifier>,
560    /// Table name
561    pub name: CqlIdentifier,
562}
563
564/// ORDER BY clause
565#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
566pub struct CqlOrderBy {
567    /// Column to order by
568    pub column: CqlIdentifier,
569    /// Sort direction
570    pub direction: CqlSortDirection,
571}
572
573/// ORDER BY ordering (alias for CqlOrderBy for compatibility)
574pub type CqlOrdering = CqlOrderBy;
575
576/// LIMIT clause
577#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
578pub struct CqlLimit {
579    /// Maximum number of rows to return
580    pub count: u64,
581}
582
583/// TTL specification
584#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
585pub struct CqlTtl {
586    /// TTL value in seconds
587    pub seconds: Option<CqlExpression>,
588}
589
590/// TIMESTAMP specification
591#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
592pub struct CqlTimestamp {
593    /// Timestamp value (microseconds since epoch)
594    pub microseconds: Option<CqlExpression>,
595}
596
597/// Sort direction
598#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
599pub enum CqlSortDirection {
600    /// Ascending order
601    Asc,
602    /// Descending order
603    Desc,
604}
605
606/// USING clause for timestamps and TTL
607#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
608pub struct CqlUsing {
609    /// TTL specification
610    pub ttl: Option<CqlExpression>,
611    /// TIMESTAMP specification
612    pub timestamp: Option<CqlExpression>,
613}
614
615/// Table options (WITH clause)
616#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
617pub struct CqlTableOptions {
618    /// Option values
619    pub options: HashMap<String, CqlLiteral>,
620}
621
622impl CqlIdentifier {
623    /// Create a new unquoted identifier
624    pub fn new(name: impl Into<String>) -> Self {
625        Self {
626            name: name.into(),
627            quoted: false,
628        }
629    }
630
631    /// Create a new quoted identifier
632    pub fn quoted(name: impl Into<String>) -> Self {
633        Self {
634            name: name.into(),
635            quoted: true,
636        }
637    }
638
639    /// Get the identifier name as a string
640    pub fn as_str(&self) -> &str {
641        &self.name
642    }
643
644    /// Get the identifier name (alias for as_str)
645    pub fn name(&self) -> &str {
646        &self.name
647    }
648
649    /// Check if the identifier is quoted
650    pub fn is_quoted(&self) -> bool {
651        self.quoted
652    }
653
654    /// Check if this identifier needs quoting
655    pub fn needs_quoting(&self) -> bool {
656        self.quoted || !self.is_valid_unquoted()
657    }
658
659    /// Check if the name is valid as an unquoted identifier
660    fn is_valid_unquoted(&self) -> bool {
661        let mut chars = self.name.chars();
662        let Some(first) = chars.next() else {
663            return false;
664        };
665        (first.is_ascii_alphabetic() || first == '_')
666            && chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
667    }
668}
669
670impl CqlTable {
671    /// Create a new table reference without keyspace
672    pub fn new(name: impl Into<String>) -> Self {
673        Self {
674            keyspace: None,
675            name: CqlIdentifier::new(name),
676        }
677    }
678
679    /// Create a new table reference with keyspace
680    pub fn with_keyspace(keyspace: impl Into<String>, name: impl Into<String>) -> Self {
681        Self {
682            keyspace: Some(CqlIdentifier::new(keyspace)),
683            name: CqlIdentifier::new(name),
684        }
685    }
686
687    /// Get the full table name (keyspace.table or just table)
688    pub fn full_name(&self) -> String {
689        match &self.keyspace {
690            Some(ks) => format!("{}.{}", ks.as_str(), self.name.as_str()),
691            None => self.name.as_str().to_string(),
692        }
693    }
694
695    /// Get the table name
696    pub fn name(&self) -> &CqlIdentifier {
697        &self.name
698    }
699
700    /// Get keyspace (returns Option<&CqlIdentifier>)
701    pub fn keyspace(&self) -> Option<&CqlIdentifier> {
702        self.keyspace.as_ref()
703    }
704}
705
706impl From<CqlDataType> for CqlType {
707    fn from(data_type: CqlDataType) -> Self {
708        match data_type {
709            CqlDataType::Boolean => CqlType::Boolean,
710            CqlDataType::TinyInt => CqlType::TinyInt,
711            CqlDataType::SmallInt => CqlType::SmallInt,
712            CqlDataType::Int => CqlType::Int,
713            CqlDataType::BigInt => CqlType::BigInt,
714            CqlDataType::Float => CqlType::Float,
715            CqlDataType::Double => CqlType::Double,
716            CqlDataType::Decimal => CqlType::Decimal,
717            CqlDataType::Text | CqlDataType::Varchar => CqlType::Text,
718            CqlDataType::Ascii => CqlType::Ascii,
719            CqlDataType::Blob => CqlType::Blob,
720            CqlDataType::Timestamp => CqlType::Timestamp,
721            CqlDataType::Date => CqlType::Date,
722            CqlDataType::Time => CqlType::Time,
723            CqlDataType::Uuid => CqlType::Uuid,
724            CqlDataType::TimeUuid => CqlType::TimeUuid,
725            CqlDataType::Inet => CqlType::Inet,
726            CqlDataType::Duration => CqlType::Duration,
727            CqlDataType::List(inner) => CqlType::List(Box::new((*inner).into())),
728            CqlDataType::Set(inner) => CqlType::Set(Box::new((*inner).into())),
729            CqlDataType::Map(key, value) => {
730                CqlType::Map(Box::new((*key).into()), Box::new((*value).into()))
731            }
732            CqlDataType::Tuple(types) => {
733                CqlType::Tuple(types.into_iter().map(|t| t.into()).collect())
734            }
735            CqlDataType::Udt(name) => CqlType::Udt(name.as_str().to_string(), vec![]),
736            CqlDataType::Frozen(inner) => CqlType::Frozen(Box::new((*inner).into())),
737            CqlDataType::Custom(name) => CqlType::Custom(name),
738            CqlDataType::Varint => CqlType::BigInt, // Map varint to bigint
739            CqlDataType::Counter => CqlType::Counter,
740        }
741    }
742}
743
744impl From<CqlType> for CqlDataType {
745    fn from(cql_type: CqlType) -> Self {
746        match cql_type {
747            CqlType::Boolean => CqlDataType::Boolean,
748            CqlType::TinyInt => CqlDataType::TinyInt,
749            CqlType::SmallInt => CqlDataType::SmallInt,
750            CqlType::Int => CqlDataType::Int,
751            CqlType::BigInt => CqlDataType::BigInt,
752            CqlType::Counter => CqlDataType::Counter,
753            CqlType::Float => CqlDataType::Float,
754            CqlType::Double => CqlDataType::Double,
755            CqlType::Decimal => CqlDataType::Decimal,
756            CqlType::Text | CqlType::Varchar => CqlDataType::Text,
757            CqlType::Ascii => CqlDataType::Ascii,
758            CqlType::Blob => CqlDataType::Blob,
759            CqlType::Timestamp => CqlDataType::Timestamp,
760            CqlType::Date => CqlDataType::Date,
761            CqlType::Time => CqlDataType::Time,
762            CqlType::Uuid => CqlDataType::Uuid,
763            CqlType::TimeUuid => CqlDataType::TimeUuid,
764            CqlType::Inet => CqlDataType::Inet,
765            CqlType::Duration => CqlDataType::Duration,
766            CqlType::Varint => CqlDataType::Custom("varint".to_string()),
767            CqlType::List(inner) => CqlDataType::List(Box::new((*inner).into())),
768            CqlType::Set(inner) => CqlDataType::Set(Box::new((*inner).into())),
769            CqlType::Map(key, value) => {
770                CqlDataType::Map(Box::new((*key).into()), Box::new((*value).into()))
771            }
772            CqlType::Tuple(types) => {
773                CqlDataType::Tuple(types.into_iter().map(|t| t.into()).collect())
774            }
775            CqlType::Udt(name, _) => CqlDataType::Udt(CqlIdentifier::new(name)),
776            CqlType::Frozen(inner) => CqlDataType::Frozen(Box::new((*inner).into())),
777            CqlType::Custom(name) => CqlDataType::Custom(name),
778        }
779    }
780}
781
782#[cfg(test)]
783mod tests {
784    use super::*;
785
786    #[test]
787    fn test_identifier_creation() {
788        let id1 = CqlIdentifier::new("test");
789        assert_eq!(id1.name, "test");
790        assert!(!id1.quoted);
791        assert!(!id1.needs_quoting());
792
793        let id2 = CqlIdentifier::quoted("test");
794        assert_eq!(id2.name, "test");
795        assert!(id2.quoted);
796        assert!(id2.needs_quoting());
797    }
798
799    #[test]
800    fn test_identifier_validation() {
801        assert!(CqlIdentifier::new("valid_name").is_valid_unquoted());
802        assert!(CqlIdentifier::new("_valid").is_valid_unquoted());
803        assert!(CqlIdentifier::new("valid123").is_valid_unquoted());
804
805        assert!(!CqlIdentifier::new("123invalid").is_valid_unquoted());
806        assert!(!CqlIdentifier::new("invalid-name").is_valid_unquoted());
807        assert!(!CqlIdentifier::new("").is_valid_unquoted());
808    }
809
810    #[test]
811    fn test_table_creation() {
812        let table1 = CqlTable::new("users");
813        assert_eq!(table1.name.as_str(), "users");
814        assert!(table1.keyspace.is_none());
815        assert_eq!(table1.full_name(), "users");
816
817        let table2 = CqlTable::with_keyspace("test", "users");
818        assert_eq!(table2.keyspace.as_ref().unwrap().as_str(), "test");
819        assert_eq!(table2.name.as_str(), "users");
820        assert_eq!(table2.full_name(), "test.users");
821    }
822
823    #[test]
824    fn test_data_type_conversion() {
825        let cql_type = CqlType::List(Box::new(CqlType::Text));
826        let data_type: CqlDataType = cql_type.clone().into();
827        let back_to_cql: CqlType = data_type.into();
828        assert_eq!(cql_type, back_to_cql);
829    }
830
831    #[test]
832    fn test_identifier_needs_quoting_rules() {
833        let numeric_start = CqlIdentifier::new("123abc");
834        assert!(numeric_start.needs_quoting());
835
836        let mixed_case = CqlIdentifier::new("CamelCase");
837        assert!(!mixed_case.needs_quoting());
838
839        let quoted = CqlIdentifier::quoted("any value");
840        assert!(quoted.needs_quoting());
841    }
842
843    #[test]
844    fn test_table_options_default_is_empty() {
845        let options = CqlTableOptions::default();
846        assert!(options.options.is_empty());
847    }
848
849    #[test]
850    fn test_varint_and_counter_mapping() {
851        let big_int = CqlType::from(CqlDataType::Varint);
852        assert_eq!(big_int, CqlType::BigInt);
853
854        let counter_type = CqlType::from(CqlDataType::Counter);
855        assert_eq!(counter_type, CqlType::Counter);
856    }
857}