Skip to main content

razeri_plugin_contract/
data_type.rs

1// Copyright (C) 2026 Gabriel Shea
2// Licensed under either of the Apache License, Version 2.0 or the MIT
3// license, at your option.
4
5//! Generic data types for cross-database compatibility.
6//!
7//! This module defines the unified [`DataType`] enum and all component types
8//! (integer, decimal, float, boolean, string, binary, date/time, special, and
9//! collection types) that serve as the foundation for SQL rendering across
10//! all supported database platforms.
11//!
12//! # Design Philosophy
13//!
14//! - **Abstract over database-specific implementations** — `DataType` captures
15//!   enough metadata to generate correct DDL for each target, but does not
16//!   embed platform-specific syntax.
17//! - **Preserve precision metadata** — Decimal types carry precision and scale;
18//!   string types carry length constraints; timestamp types carry fractional
19//!   seconds precision.
20//! - **Support intersection of capabilities** — All types in the enum can be
21//!   rendered by all 8 dialect plugins, even if some platforms emulate a type
22//!   (e.g., JSON stored as `NVARCHAR(MAX)` in SQL Server).
23//!
24//! # Type Categories
25//!
26//! | Category | Types | Example |
27//! |----------|-------|---------|
28//! | Integer | [`IntegerDataType`] | `INT`, `BIGINT`, `TINYINT` |
29//! | Decimal | [`DecimalDataType`] | `DECIMAL(19,4)`, `DECIMAL(5,2)` |
30//! | Float | [`FloatDataType`] | `REAL`, `DOUBLE` |
31//! | Boolean | [`BooleanDataType`] | `BOOLEAN`, `BIT` |
32//! | String | [`CharDataType`], [`VarCharDataType`], [`TextDataType`] | `VARCHAR(100)`, `TEXT` |
33//! | Binary | [`BinaryDataType`], [`VarBinaryDataType`] | `BYTEA`, `VARBINARY` |
34//! | Date/Time | [`DateDataType`], [`TimestampDataType`], [`TimestampTzDataType`] | `DATE`, `TIMESTAMP` |
35//! | Special | [`UuidDataType`], [`JsonDataType`] | `UUID`, `JSONB` |
36//! | Collection | [`ArrayDataType`], [`MapDataType`] | `INTEGER[]`, `MAP<TEXT,INT>` |
37//!
38//! # Usage
39//!
40//! Convenience constructors for common types:
41//!
42//! ```rust
43//! use razeri_plugin_contract::DataType;
44//!
45//! let id = DataType::bigint();
46//! let name = DataType::varchar(255);
47//! let price = DataType::decimal(19, 4);
48//! let created = DataType::timestamp();
49//!
50//! // JSON with binary storage (JSONB in PostgreSQL, VARIANT in Snowflake)
51//! let data = DataType::json();
52//! ```
53//!
54//! Each variant maps to platform-specific types as documented in the variant
55//! definitions. Dialect plugins resolve the final SQL type string during
56//! rendering; the rendering traits live in this crate's `renderer` module.
57
58// ============================================================================
59// Integer Types
60// ============================================================================
61
62/// Integer size variants matching common database integer types.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64pub enum IntegerSize {
65    /// 1 byte: -128 to 127 (signed) or 0 to 255 (unsigned)
66    /// Maps to: TINYINT (SQL Server, Snowflake, Databricks), SMALLINT (`PostgreSQL`), Int8/UInt8 (`ClickHouse`)
67    Tiny,
68    /// 2 bytes: -32,768 to 32,767 (signed)
69    /// Maps to: SMALLINT (all databases), Int16/UInt16 (`ClickHouse`)
70    Small,
71    /// 4 bytes: -2.1B to 2.1B (signed)
72    /// Maps to: INT/INTEGER (all databases), Int32/UInt32 (`ClickHouse`)
73    Standard,
74    /// 8 bytes: -9.2E18 to 9.2E18 (signed)
75    /// Maps to: BIGINT (all databases), Int64/UInt64 (`ClickHouse`)
76    Big,
77}
78
79/// Integer data type supporting signed/unsigned variants.
80///
81/// Most databases only support signed integers. `ClickHouse` is the exception,
82/// supporting unsigned variants via `UInt8`, `UInt16`, `UInt32`, `UInt64`.
83/// Other dialect plugins will map unsigned types to the next larger signed type
84/// when unsigned is not supported.
85///
86/// # Example
87///
88/// ```rust
89/// use razeri_plugin_contract::{DataType, data_type::{IntegerDataType, IntegerSize}};
90///
91/// // Signed 4-byte integer (INT/INTEGER on most platforms)
92/// let _id = DataType::int();
93///
94/// // Unsigned 8-byte integer (UInt64 in ClickHouse, BIGINT elsewhere)
95/// let big = IntegerDataType::unsigned(IntegerSize::Big);
96/// assert_eq!(big.byte_size(), 8);
97/// ```
98#[derive(Debug, Clone, PartialEq, Eq, Hash)]
99pub struct IntegerDataType {
100    /// Size of the integer (Tiny, Small, Standard, Big).
101    pub size: IntegerSize,
102
103    /// Whether the integer is unsigned.
104    ///
105    /// Only `ClickHouse` fully supports unsigned integers. Other dialect plugins
106    /// will map unsigned types to the next larger signed type when unsigned is
107    /// not supported by the target platform.
108    ///
109    /// # Example
110    ///
111    /// ```rust
112    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
113    ///
114    /// let signed = IntegerDataType::new(IntegerSize::Standard);
115    /// assert!(!signed.unsigned);
116    ///
117    /// let unsigned = IntegerDataType::unsigned(IntegerSize::Big);
118    /// assert!(unsigned.unsigned);
119    /// ```
120    pub unsigned: bool,
121}
122
123impl IntegerDataType {
124    /// Creates a new signed integer with the given [`IntegerSize`].
125    ///
126    /// # Example
127    ///
128    /// ```rust
129    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
130    ///
131    /// let small = IntegerDataType::new(IntegerSize::Small);
132    /// assert_eq!(small.byte_size(), 2);
133    /// ```
134    #[must_use]
135    pub fn new(size: IntegerSize) -> Self {
136        Self { size, unsigned: false }
137    }
138
139    /// Creates a new unsigned integer with the given [`IntegerSize`].
140    ///
141    /// # Example
142    ///
143    /// ```rust
144    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
145    ///
146    /// let unsigned_big = IntegerDataType::unsigned(IntegerSize::Big);
147    /// assert!(unsigned_big.unsigned);
148    /// assert_eq!(unsigned_big.byte_size(), 8);
149    /// ```
150    #[must_use]
151    pub fn unsigned(size: IntegerSize) -> Self {
152        Self { size, unsigned: true }
153    }
154
155    /// Returns the byte size of this integer type.
156    ///
157    /// # Example
158    ///
159    /// ```rust
160    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
161    ///
162    /// assert_eq!(IntegerDataType::new(IntegerSize::Tiny).byte_size(), 1);
163    /// assert_eq!(IntegerDataType::new(IntegerSize::Small).byte_size(), 2);
164    /// assert_eq!(IntegerDataType::new(IntegerSize::Standard).byte_size(), 4);
165    /// assert_eq!(IntegerDataType::new(IntegerSize::Big).byte_size(), 8);
166    /// ```
167    #[must_use]
168    pub fn byte_size(&self) -> usize {
169        match self.size {
170            IntegerSize::Tiny => 1,
171            IntegerSize::Small => 2,
172            IntegerSize::Standard => 4,
173            IntegerSize::Big => 8,
174        }
175    }
176}
177
178// ============================================================================
179// Decimal/Numeric Types (Exact Precision)
180// ============================================================================
181
182/// Exact numeric type with fixed precision and scale.
183///
184/// Maps to: `NUMERIC`/`DECIMAL` (`PostgreSQL`, SQL Server), `NUMBER` (Snowflake),
185/// `DECIMAL` (Databricks), `Decimal` (`ClickHouse`).
186///
187/// # Constraints
188///
189/// - `precision`: 1–38 (most databases), up to 76 (some platforms)
190/// - `scale`: 0 to `precision`
191/// - Panics if `scale > precision` or `precision > 38`
192///
193/// # Example
194///
195/// ```rust
196/// use razeri_plugin_contract::data_type::DecimalDataType;
197///
198/// // Money: DECIMAL(19, 4)
199/// let money = DecimalDataType::money();
200/// assert_eq!(money.precision, 19);
201/// assert_eq!(money.scale, 4);
202///
203/// // Percentage: DECIMAL(5, 2)
204/// let pct = DecimalDataType::percentage();
205/// assert_eq!(pct.precision, 5);
206/// assert_eq!(pct.scale, 2);
207///
208/// // Custom decimal
209/// let custom = DecimalDataType::new(10, 2);
210/// assert_eq!(custom.precision, 10);
211/// assert_eq!(custom.scale, 2);
212/// ```
213#[derive(Debug, Clone, PartialEq, Eq, Hash)]
214pub struct DecimalDataType {
215    /// Total number of digits (1–38 for most databases, up to 76 for some).
216    pub precision: u8,
217
218    /// Number of digits after the decimal point (0 to `precision`).
219    pub scale: u8,
220}
221
222impl DecimalDataType {
223    /// Creates a new decimal type with the given precision and scale.
224    ///
225    /// # Panics
226    ///
227    /// Panics if `precision` is 0 or exceeds 38, or if `scale > precision`.
228    ///
229    /// # Example
230    ///
231    /// ```rust
232    /// use razeri_plugin_contract::data_type::DecimalDataType;
233    ///
234    /// let dec = DecimalDataType::new(10, 2);
235    /// assert_eq!(dec.precision, 10);
236    /// assert_eq!(dec.scale, 2);
237    /// ```
238    ///
239    /// # Panics
240    ///
241    /// ```should_panic
242    /// use razeri_plugin_contract::data_type::DecimalDataType;
243    ///
244    /// // Precision must be 1-38
245    /// let _ = DecimalDataType::new(0, 0); // panics
246    ///
247    /// // Scale cannot exceed precision
248    /// let _ = DecimalDataType::new(5, 10); // panics
249    /// ```
250    #[must_use]
251    pub fn new(precision: u8, scale: u8) -> Self {
252        assert!(precision > 0 && precision <= 38, "Precision must be 1-38");
253        assert!(scale <= precision, "Scale cannot exceed precision");
254        Self { precision, scale }
255    }
256
257    /// Creates the common money type: `DECIMAL(19, 4)`.
258    ///
259    /// # Example
260    ///
261    /// ```rust
262    /// use razeri_plugin_contract::data_type::DecimalDataType;
263    ///
264    /// let money = DecimalDataType::money();
265    /// assert_eq!(money.precision, 19);
266    /// assert_eq!(money.scale, 4);
267    /// ```
268    #[must_use]
269    pub fn money() -> Self {
270        Self { precision: 19, scale: 4 }
271    }
272
273    /// Creates a percentage type with 2 decimal places: `DECIMAL(5, 2)`.
274    ///
275    /// # Example
276    ///
277    /// ```rust
278    /// use razeri_plugin_contract::data_type::DecimalDataType;
279    ///
280    /// let pct = DecimalDataType::percentage();
281    /// assert_eq!(pct.precision, 5);
282    /// assert_eq!(pct.scale, 2);
283    /// ```
284    #[must_use]
285    pub fn percentage() -> Self {
286        Self { precision: 5, scale: 2 }
287    }
288}
289
290// ============================================================================
291// Floating Point Types (Approximate)
292// ============================================================================
293
294/// Floating point precision variants.
295///
296/// # Example
297///
298/// ```rust
299/// use razeri_plugin_contract::data_type::FloatPrecision;
300///
301/// let single = FloatPrecision::Single;
302/// let double = FloatPrecision::Double;
303///
304/// assert_eq!(format!("{:?}", single), "Single");
305/// assert_eq!(format!("{:?}", double), "Double");
306/// ```
307#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
308pub enum FloatPrecision {
309    /// Single precision (4 bytes, ~7 decimal digits).
310    ///
311    /// Maps to: `REAL`/`FLOAT4` (`PostgreSQL`), `REAL`/`FLOAT(24)` (SQL Server),
312    /// `FLOAT` (Snowflake, Databricks), `Float32` (`ClickHouse`).
313    Single,
314    /// Double precision (8 bytes, ~15 decimal digits).
315    ///
316    /// Maps to: `DOUBLE PRECISION`/`FLOAT8` (`PostgreSQL`), `FLOAT` (SQL Server),
317    /// `DOUBLE` (Snowflake, Databricks), `Float64` (`ClickHouse`).
318    Double,
319}
320
321/// Approximate floating-point numeric type.
322///
323/// Maps to: `REAL`/`DOUBLE PRECISION` (`PostgreSQL`), `REAL`/`FLOAT` (SQL Server),
324/// `FLOAT`/`DOUBLE` (Snowflake, Databricks), `Float32`/`Float64` (`ClickHouse`).
325///
326/// # Example
327///
328/// ```
329/// use razeri_plugin_contract::DataType;
330///
331/// // Single precision (REAL)
332/// let real = DataType::float();
333///
334/// // Double precision (DOUBLE)
335/// let double = DataType::double();
336/// ```
337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
338pub struct FloatDataType {
339    /// Precision level (Single or Double).
340    pub precision: FloatPrecision,
341}
342
343impl FloatDataType {
344    /// Creates a single-precision (4-byte) float type.
345    ///
346    /// # Example
347    ///
348    /// ```rust
349    /// use razeri_plugin_contract::data_type::{FloatDataType, FloatPrecision};
350    ///
351    /// let single = FloatDataType::single();
352    /// assert_eq!(single.precision, FloatPrecision::Single);
353    /// ```
354    #[must_use]
355    pub fn single() -> Self {
356        Self { precision: FloatPrecision::Single }
357    }
358
359    /// Creates a double-precision (8-byte) float type.
360    ///
361    /// # Example
362    ///
363    /// ```rust
364    /// use razeri_plugin_contract::data_type::{FloatDataType, FloatPrecision};
365    ///
366    /// let double = FloatDataType::double();
367    /// assert_eq!(double.precision, FloatPrecision::Double);
368    /// ```
369    #[must_use]
370    pub fn double() -> Self {
371        Self { precision: FloatPrecision::Double }
372    }
373}
374
375// ============================================================================
376// Boolean Type
377// ============================================================================
378
379/// Boolean data type (true/false).
380///
381/// Maps to: `BOOLEAN` (`PostgreSQL`, Snowflake, Databricks, `ClickHouse`),
382/// `BIT` (SQL Server), `Bool`/`UInt8` (`ClickHouse`).
383///
384/// # Example
385///
386/// ```
387/// use razeri_plugin_contract::DataType;
388///
389/// let bool_type = DataType::boolean();
390/// ```
391#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
392pub struct BooleanDataType;
393
394// ============================================================================
395// String/Text Types
396// ============================================================================
397
398/// Character encoding for string types.
399///
400/// Determines whether strings use ASCII/Latin1 (1 byte per character) or
401/// UTF-8/Unicode (variable bytes per character). The default is `Unicode`.
402///
403/// # Example
404///
405/// ```rust
406/// use razeri_plugin_contract::data_type::StringEncoding;
407///
408/// let ascii = StringEncoding::Ascii;
409/// let unicode = StringEncoding::Unicode;
410///
411/// assert_eq!(ascii, StringEncoding::Ascii);
412/// assert_eq!(unicode, StringEncoding::Unicode);
413/// ```
414#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
415pub enum StringEncoding {
416    /// ASCII/Latin1 encoding (1 byte per character).
417    ///
418    /// Maps to: `CHAR`/`VARCHAR` (SQL Server), `CHAR`/`VARCHAR` (`PostgreSQL`).
419    Ascii,
420    /// UTF-8/Unicode encoding (variable bytes per character).
421    ///
422    /// Maps to: `NCHAR`/`NVARCHAR` (SQL Server), all strings in
423    /// `PostgreSQL`/Snowflake/`ClickHouse`.
424    #[default]
425    Unicode,
426}
427
428/// Fixed-length character string.
429///
430/// Maps to: `CHAR(n)` (`PostgreSQL`, SQL Server, Snowflake), `FixedString(n)` (`ClickHouse`).
431/// Note: Databricks does not have a fixed-length char type.
432///
433/// # Example
434///
435/// ```rust
436/// use razeri_plugin_contract::data_type::{CharDataType, StringEncoding};
437///
438/// // Default: Unicode encoding
439/// let char_type = CharDataType::new(50);
440/// assert_eq!(char_type.length, 50);
441///
442/// // Explicit ASCII encoding
443/// let ascii_char = CharDataType::with_encoding(10, StringEncoding::Ascii);
444/// ```
445#[derive(Debug, Clone, PartialEq, Eq, Hash)]
446pub struct CharDataType {
447    /// Fixed length in characters (1-8000 for SQL Server, 1-16777216 for Snowflake).
448    pub length: u32,
449
450    /// Character encoding (default: Unicode).
451    pub encoding: StringEncoding,
452}
453
454impl CharDataType {
455    /// Creates a fixed-length character string with Unicode encoding.
456    ///
457    /// # Example
458    ///
459    /// ```rust
460    /// use razeri_plugin_contract::data_type::CharDataType;
461    ///
462    /// let char = CharDataType::new(100);
463    /// assert_eq!(char.length, 100);
464    /// ```
465    #[must_use]
466    pub fn new(length: u32) -> Self {
467        Self { length, encoding: StringEncoding::Unicode }
468    }
469
470    /// Creates a fixed-length character string with the specified encoding.
471    ///
472    /// # Example
473    ///
474    /// ```rust
475    /// use razeri_plugin_contract::data_type::{CharDataType, StringEncoding};
476    ///
477    /// let ascii = CharDataType::with_encoding(20, StringEncoding::Ascii);
478    /// assert_eq!(ascii.encoding, StringEncoding::Ascii);
479    /// ```
480    #[must_use]
481    pub fn with_encoding(length: u32, encoding: StringEncoding) -> Self {
482        Self { length, encoding }
483    }
484}
485
486/// Variable-length character string with optional max length.
487///
488/// Maps to: `VARCHAR(n)` (`PostgreSQL`, SQL Server, Snowflake), `STRING` (Databricks, `ClickHouse`).
489///
490/// # Example
491///
492/// ```rust
493/// use razeri_plugin_contract::data_type::{VarCharDataType, StringEncoding};
494///
495/// // Fixed max length
496/// let varchar = VarCharDataType::new(255);
497/// assert_eq!(varchar.max_length, Some(255));
498///
499/// // Unlimited length (TEXT/VARCHAR(MAX)/STRING)
500/// let text = VarCharDataType::unlimited();
501/// assert_eq!(text.max_length, None);
502///
503/// // Explicit ASCII encoding
504/// let ascii = VarCharDataType::with_encoding(Some(100), StringEncoding::Ascii);
505/// ```
506#[derive(Debug, Clone, PartialEq, Eq, Hash)]
507pub struct VarCharDataType {
508    /// Maximum length in characters (None = unlimited/MAX).
509    ///
510    /// Platform limits:
511    /// - `PostgreSQL`: up to 10,485,760
512    /// - SQL Server: up to 8,000 (or MAX)
513    /// - Snowflake: up to 16,777,216
514    /// - `ClickHouse`: unlimited
515    pub max_length: Option<u32>,
516
517    /// Character encoding (default: Unicode).
518    pub encoding: StringEncoding,
519}
520
521impl VarCharDataType {
522    /// Creates a VARCHAR with specified max length.
523    ///
524    /// # Example
525    ///
526    /// ```rust
527    /// use razeri_plugin_contract::data_type::VarCharDataType;
528    ///
529    /// let varchar = VarCharDataType::new(100);
530    /// assert_eq!(varchar.max_length, Some(100));
531    /// ```
532    #[must_use]
533    pub fn new(max_length: u32) -> Self {
534        Self { max_length: Some(max_length), encoding: StringEncoding::Unicode }
535    }
536
537    /// Creates an unlimited length text type (`VARCHAR(MAX)`, `TEXT`, `STRING`).
538    ///
539    /// # Example
540    ///
541    /// ```rust
542    /// use razeri_plugin_contract::data_type::VarCharDataType;
543    ///
544    /// let text = VarCharDataType::unlimited();
545    /// assert_eq!(text.max_length, None);
546    /// ```
547    #[must_use]
548    pub fn unlimited() -> Self {
549        Self { max_length: None, encoding: StringEncoding::Unicode }
550    }
551
552    /// Creates a VARCHAR with the specified length and encoding.
553    ///
554    /// # Example
555    ///
556    /// ```rust
557    /// use razeri_plugin_contract::data_type::{VarCharDataType, StringEncoding};
558    ///
559    /// let ascii = VarCharDataType::with_encoding(Some(50), StringEncoding::Ascii);
560    /// assert_eq!(ascii.max_length, Some(50));
561    /// assert_eq!(ascii.encoding, StringEncoding::Ascii);
562    /// ```
563    #[must_use]
564    pub fn with_encoding(max_length: Option<u32>, encoding: StringEncoding) -> Self {
565        Self { max_length, encoding }
566    }
567}
568
569/// Unbounded text type for large text storage.
570///
571/// Maps to: `TEXT` (`PostgreSQL`), `VARCHAR(MAX)` (SQL Server),
572/// `STRING` (Snowflake, Databricks, `ClickHouse`).
573///
574/// # Example
575///
576/// ```
577/// use razeri_plugin_contract::DataType;
578///
579/// let text = DataType::text();
580/// ```
581#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
582pub struct TextDataType;
583
584// ============================================================================
585// Binary Types
586// ============================================================================
587
588/// Fixed-length binary data.
589///
590/// Maps to: `BYTEA` (`PostgreSQL`), `BINARY(n)` (SQL Server, Snowflake),
591/// `FixedString(n)` (`ClickHouse`).
592///
593/// # Example
594///
595/// ```rust
596/// use razeri_plugin_contract::data_type::BinaryDataType;
597///
598/// let binary = BinaryDataType::new(256);
599/// assert_eq!(binary.length, 256);
600/// ```
601#[derive(Debug, Clone, PartialEq, Eq, Hash)]
602pub struct BinaryDataType {
603    /// Fixed length in bytes.
604    pub length: u32,
605}
606
607impl BinaryDataType {
608    /// Creates a fixed-length binary type with the given length in bytes.
609    ///
610    /// # Example
611    ///
612    /// ```rust
613    /// use razeri_plugin_contract::data_type::BinaryDataType;
614    ///
615    /// let binary = BinaryDataType::new(128);
616    /// assert_eq!(binary.length, 128);
617    /// ```
618    #[must_use]
619    pub fn new(length: u32) -> Self {
620        Self { length }
621    }
622}
623
624/// Variable-length binary data.
625///
626/// Maps to: `BYTEA` (`PostgreSQL`), `VARBINARY(n)` (SQL Server, Snowflake),
627/// `String` (`ClickHouse`), `BINARY` (Databricks).
628///
629/// # Example
630///
631/// ```rust
632/// use razeri_plugin_contract::data_type::VarBinaryDataType;
633///
634/// // Fixed max length
635/// let vb = VarBinaryDataType::new(1024);
636/// assert_eq!(vb.max_length, Some(1024));
637///
638/// // Unlimited length
639/// let blob = VarBinaryDataType::unlimited();
640/// assert_eq!(blob.max_length, None);
641/// ```
642#[derive(Debug, Clone, PartialEq, Eq, Hash)]
643pub struct VarBinaryDataType {
644    /// Maximum length in bytes (None = unlimited/MAX).
645    pub max_length: Option<u32>,
646}
647
648impl VarBinaryDataType {
649    /// Creates a variable-length binary type with the specified max length in bytes.
650    ///
651    /// # Example
652    ///
653    /// ```rust
654    /// use razeri_plugin_contract::data_type::VarBinaryDataType;
655    ///
656    /// let vb = VarBinaryDataType::new(2048);
657    /// assert_eq!(vb.max_length, Some(2048));
658    /// ```
659    #[must_use]
660    pub fn new(max_length: u32) -> Self {
661        Self { max_length: Some(max_length) }
662    }
663
664    /// Creates an unlimited-length binary type (BYTEA, VARBINARY(MAX), etc.).
665    ///
666    /// # Example
667    ///
668    /// ```rust
669    /// use razeri_plugin_contract::data_type::VarBinaryDataType;
670    ///
671    /// let blob = VarBinaryDataType::unlimited();
672    /// assert_eq!(blob.max_length, None);
673    /// ```
674    #[must_use]
675    pub fn unlimited() -> Self {
676        Self { max_length: None }
677    }
678}
679
680// ============================================================================
681// Date and Time Types
682// ============================================================================
683
684/// Date only (year, month, day) without time component.
685///
686/// Maps to: `DATE` (all databases), `Date`/`Date32` (`ClickHouse`).
687///
688/// # Example
689///
690/// ```
691/// use razeri_plugin_contract::DataType;
692///
693/// let date = DataType::date();
694/// ```
695#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
696pub struct DateDataType;
697
698/// Time only without date component.
699///
700/// Maps to: `TIME` (`PostgreSQL`, SQL Server, Snowflake), not directly supported in
701/// `ClickHouse`/Databricks.
702///
703/// # Precision
704///
705/// - `PostgreSQL`: 0-6
706/// - SQL Server: 0-7
707/// - Snowflake: 0-9
708/// - `ClickHouse`: 0-9
709///
710/// # Example
711///
712/// ```rust
713/// use razeri_plugin_contract::data_type::TimeDataType;
714///
715/// // Default precision: 6, no timezone
716/// let time = TimeDataType::default();
717/// assert_eq!(time.precision, 6);
718/// assert!(!time.with_timezone);
719///
720/// // Precision 3, with timezone
721/// let tz_time = TimeDataType::with_timezone(3);
722/// assert_eq!(tz_time.precision, 3);
723/// assert!(tz_time.with_timezone);
724/// ```
725#[derive(Debug, Clone, PartialEq, Eq, Hash)]
726pub struct TimeDataType {
727    /// Fractional seconds precision (0-7 for SQL Server, 0-6 for `PostgreSQL`, 0-9 for Snowflake).
728    pub precision: u8,
729
730    /// Whether to include timezone offset.
731    ///
732    /// Maps to: `TIME WITH TIME ZONE` (`PostgreSQL`), not supported in SQL Server.
733    pub with_timezone: bool,
734}
735
736impl TimeDataType {
737    /// Creates a new `TimeDataType` with the given fractional seconds precision.
738    ///
739    /// # Panics
740    ///
741    /// Panics if `precision` exceeds 9.
742    ///
743    /// # Example
744    ///
745    /// ```rust
746    /// use razeri_plugin_contract::data_type::TimeDataType;
747    ///
748    /// let time = TimeDataType::new(3);
749    /// assert_eq!(time.precision, 3);
750    /// assert!(!time.with_timezone);
751    /// ```
752    ///
753    /// ```rust,should_panic
754    /// use razeri_plugin_contract::data_type::TimeDataType;
755    ///
756    /// let _ = TimeDataType::new(10); // panics: precision must be 0-9
757    /// ```
758    #[must_use]
759    pub fn new(precision: u8) -> Self {
760        assert!(precision <= 9, "Time precision must be 0-9");
761        Self { precision, with_timezone: false }
762    }
763
764    /// Creates a `TimeDataType` with timezone and the given precision.
765    ///
766    /// # Panics
767    ///
768    /// Panics if `precision` exceeds 9.
769    ///
770    /// # Example
771    ///
772    /// ```rust
773    /// use razeri_plugin_contract::data_type::TimeDataType;
774    ///
775    /// let tz_time = TimeDataType::with_timezone(3);
776    /// assert_eq!(tz_time.precision, 3);
777    /// assert!(tz_time.with_timezone);
778    /// ```
779    #[must_use]
780    pub fn with_timezone(precision: u8) -> Self {
781        assert!(precision <= 9, "Time precision must be 0-9");
782        Self { precision, with_timezone: true }
783    }
784}
785
786impl Default for TimeDataType {
787    fn default() -> Self {
788        Self { precision: 6, with_timezone: false }
789    }
790}
791
792/// Timestamp (date and time) without timezone.
793///
794/// Maps to: `TIMESTAMP` (`PostgreSQL`), `DATETIME2` (SQL Server),
795/// `TIMESTAMP_NTZ` (Snowflake), `TIMESTAMP` (Databricks), `DateTime` (`ClickHouse`).
796///
797/// # Precision
798///
799/// - `PostgreSQL`: 0-6
800/// - SQL Server: 0-7
801/// - Snowflake: 0-9
802/// - `ClickHouse`: 0-9
803///
804/// # Example
805///
806/// ```rust
807/// use razeri_plugin_contract::data_type::{DataType, TimestampDataType};
808///
809/// // Default: precision 6
810/// let ts = TimestampDataType::default();
811/// assert_eq!(ts.precision, 6);
812///
813/// // Custom precision
814/// let ts3 = TimestampDataType::new(3);
815/// assert_eq!(ts3.precision, 3);
816///
817/// // Via DataType convenience
818/// let created = DataType::timestamp();
819/// ```
820#[derive(Debug, Clone, PartialEq, Eq, Hash)]
821pub struct TimestampDataType {
822    /// Fractional seconds precision (0-9).
823    ///
824    /// - `PostgreSQL`: 0-6
825    /// - SQL Server: 0-7
826    /// - Snowflake: 0-9
827    /// - `ClickHouse`: 0-9
828    pub precision: u8,
829}
830
831impl TimestampDataType {
832    /// Creates a new `TimestampDataType` with the given fractional seconds precision.
833    ///
834    /// # Panics
835    ///
836    /// Panics if `precision` exceeds 9.
837    ///
838    /// # Example
839    ///
840    /// ```rust
841    /// use razeri_plugin_contract::data_type::TimestampDataType;
842    ///
843    /// let ts = TimestampDataType::new(3);
844    /// assert_eq!(ts.precision, 3);
845    /// ```
846    ///
847    /// ```should_panic
848    /// use razeri_plugin_contract::data_type::TimestampDataType;
849    ///
850    /// let _ = TimestampDataType::new(10); // panics: precision must be 0-9
851    /// ```
852    #[must_use]
853    pub fn new(precision: u8) -> Self {
854        assert!(precision <= 9, "Timestamp precision must be 0-9");
855        Self { precision }
856    }
857}
858
859impl Default for TimestampDataType {
860    fn default() -> Self {
861        Self { precision: 6 }
862    }
863}
864
865/// Timestamp with timezone awareness.
866///
867/// Maps to: `TIMESTAMPTZ` (`PostgreSQL`), `DATETIMEOFFSET` (SQL Server),
868/// `TIMESTAMP_TZ`/`TIMESTAMP_LTZ` (Snowflake), `TIMESTAMP` (Databricks),
869/// `DateTime64` (`ClickHouse`).
870///
871/// # Example
872///
873/// ```rust
874/// use razeri_plugin_contract::DataType;
875/// use razeri_plugin_contract::data_type::TimestampTzDataType;
876///
877/// // Default: precision 6
878/// let ts = TimestampTzDataType::default();
879/// assert_eq!(ts.precision, 6);
880///
881/// // Via DataType convenience
882/// let created_tz = DataType::timestamp_tz();
883/// ```
884#[derive(Debug, Clone, PartialEq, Eq, Hash)]
885pub struct TimestampTzDataType {
886    /// Fractional seconds precision (0-9).
887    pub precision: u8,
888}
889
890impl TimestampTzDataType {
891    /// Creates a new `TimestampTzDataType` with the given fractional seconds precision.
892    ///
893    /// # Panics
894    ///
895    /// Panics if `precision` exceeds 9.
896    ///
897    /// # Example
898    ///
899    /// ```rust
900    /// use razeri_plugin_contract::data_type::TimestampTzDataType;
901    ///
902    /// let ts = TimestampTzDataType::new(3);
903    /// assert_eq!(ts.precision, 3);
904    /// ```
905    ///
906    /// ```should_panic
907    /// use razeri_plugin_contract::data_type::TimestampTzDataType;
908    ///
909    /// let _ = TimestampTzDataType::new(10); // panics: precision must be 0-9
910    /// ```
911    #[must_use]
912    pub fn new(precision: u8) -> Self {
913        assert!(precision <= 9, "Timestamp precision must be 0-9");
914        Self { precision }
915    }
916}
917
918impl Default for TimestampTzDataType {
919    fn default() -> Self {
920        Self { precision: 6 }
921    }
922}
923
924/// Combined date and time for legacy compatibility.
925///
926/// Maps to: `DATETIME` (SQL Server legacy), `TIMESTAMP` (`PostgreSQL`).
927/// Prefer [`TimestampDataType`] for new designs.
928///
929/// # Example
930///
931/// ```rust
932/// use razeri_plugin_contract::data_type::DateTimeDataType;
933///
934/// let dt = DateTimeDataType::new(3, true);
935/// assert_eq!(dt.precision, 3);
936/// assert!(dt.with_timezone);
937/// ```
938#[derive(Debug, Clone, PartialEq, Eq, Hash)]
939pub struct DateTimeDataType {
940    /// Fractional seconds precision (0-9).
941    pub precision: u8,
942
943    /// Whether to include timezone offset.
944    pub with_timezone: bool,
945}
946
947impl DateTimeDataType {
948    /// Creates a new `DateTimeDataType` with the given precision and timezone flag.
949    ///
950    /// # Example
951    ///
952    /// ```rust
953    /// use razeri_plugin_contract::data_type::DateTimeDataType;
954    ///
955    /// let dt = DateTimeDataType::new(6, false);
956    /// assert_eq!(dt.precision, 6);
957    /// assert!(!dt.with_timezone);
958    /// ```
959    #[must_use]
960    pub fn new(precision: u8, with_timezone: bool) -> Self {
961        Self { precision, with_timezone }
962    }
963}
964
965impl Default for DateTimeDataType {
966    fn default() -> Self {
967        Self { precision: 6, with_timezone: false }
968    }
969}
970
971/// Interval/duration type for time spans.
972///
973/// Maps to: `INTERVAL` (`PostgreSQL`, Snowflake, Databricks), not directly supported in
974/// SQL Server/`ClickHouse`.
975///
976/// # Example
977///
978/// ```rust
979/// use razeri_plugin_contract::data_type::IntervalDataType;
980///
981/// let ym = IntervalDataType::YearMonth;
982/// let dt = IntervalDataType::DayTime;
983///
984/// assert_eq!(format!("{:?}", ym), "YearMonth");
985/// assert_eq!(format!("{:?}", dt), "DayTime");
986/// ```
987#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
988pub enum IntervalDataType {
989    /// Year to month intervals.
990    YearMonth,
991    /// Day to second intervals (with optional fractional seconds precision).
992    DayTime,
993}
994
995// ============================================================================
996// Special Types
997// ============================================================================
998
999/// Universally Unique Identifier (UUID/GUID).
1000///
1001/// Maps to: `UUID` (`PostgreSQL`), `UNIQUEIDENTIFIER` (SQL Server),
1002/// `STRING` (Snowflake, Databricks), `UUID` (`ClickHouse`).
1003///
1004/// # Example
1005///
1006/// ```
1007/// use razeri_plugin_contract::DataType;
1008///
1009/// let uuid = DataType::uuid();
1010/// ```
1011#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1012pub struct UuidDataType;
1013
1014/// JSON storage format preference.
1015///
1016/// Determines whether JSON data is stored as text (preserving whitespace and key order)
1017/// or in a binary-optimized format (normalized, faster queries, indexable).
1018///
1019/// # Example
1020///
1021/// ```rust
1022/// use razeri_plugin_contract::data_type::JsonStorageFormat;
1023///
1024/// let text = JsonStorageFormat::Text;
1025/// let binary = JsonStorageFormat::Binary;
1026///
1027/// assert_eq!(format!("{:?}", text), "Text");
1028/// assert_eq!(format!("{:?}", binary), "Binary");
1029/// assert!(!text.is_binary());
1030/// assert!(binary.is_binary());
1031/// ```
1032#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1033pub enum JsonStorageFormat {
1034    /// Text-based JSON storage (preserves whitespace, key order).
1035    ///
1036    /// Maps to: `JSON` (`PostgreSQL`), `NVARCHAR(MAX)` (SQL Server),
1037    /// `STRING` (Databricks).
1038    Text,
1039    /// Binary-optimized JSON storage (normalized, faster queries, indexable).
1040    ///
1041    /// Maps to: `JSONB` (`PostgreSQL`), `VARIANT` (Snowflake), `JSON` (`ClickHouse`).
1042    ///
1043    /// This is the recommended default for most use cases.
1044    #[default]
1045    Binary,
1046}
1047
1048impl JsonStorageFormat {
1049    /// Returns `true` if this is the binary storage format.
1050    ///
1051    /// # Example
1052    ///
1053    /// ```rust
1054    /// use razeri_plugin_contract::data_type::JsonStorageFormat;
1055    ///
1056    /// assert!(!JsonStorageFormat::Text.is_binary());
1057    /// assert!(JsonStorageFormat::Binary.is_binary());
1058    /// ```
1059    #[must_use]
1060    pub const fn is_binary(&self) -> bool {
1061        matches!(self, Self::Binary)
1062    }
1063}
1064
1065/// JSON/Semi-structured data type.
1066///
1067/// Database mapping details:
1068///
1069/// | Platform | Type | Notes |
1070/// |----------|------|-------|
1071/// | **`PostgreSQL`** | `JSON` or `JSONB` | JSONB supports GIN indexing and is faster for queries |
1072/// | **SQL Server** | `NVARCHAR(MAX)` with JSON functions (2016+), or native JSON (2025+) | |
1073/// | **Snowflake** | `VARIANT` | Native semi-structured type, supports nested data and arrays |
1074/// | **Databricks** | `STRING` with JSON functions, or `VARIANT` | |
1075/// | **`ClickHouse`** | `JSON` (experimental/production), or `String` with JSON functions | |
1076/// | **`CitusDB`** | Same as `PostgreSQL` (JSONB recommended) | |
1077///
1078/// # Storage Formats
1079///
1080/// - **Binary** (default): Normalized storage, faster queries, indexable.
1081///   Maps to `JSONB` in `PostgreSQL`, `VARIANT` in Snowflake.
1082/// - **Text**: Preserves whitespace, key order, and duplicate keys.
1083///   Maps to `JSON` in `PostgreSQL`, `NVARCHAR(MAX)` in SQL Server.
1084///
1085/// # Example
1086///
1087/// ```rust
1088/// use razeri_plugin_contract::DataType;
1089/// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1090///
1091/// // Binary format (recommended) - uses JSONB in PostgreSQL, VARIANT in Snowflake
1092/// let json_binary = JsonDataType::default();
1093/// assert_eq!(json_binary.storage_format, JsonStorageFormat::Binary);
1094///
1095/// // Text format - preserves original formatting
1096/// let json_text = JsonDataType::text();
1097/// assert_eq!(json_text.storage_format, JsonStorageFormat::Text);
1098///
1099/// // Via DataType convenience
1100/// let data = DataType::json();
1101/// ```
1102#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
1103pub struct JsonDataType {
1104    /// Storage format preference (binary is recommended for most use cases).
1105    pub storage_format: JsonStorageFormat,
1106
1107    /// Whether to validate JSON on insert (if supported by the database).
1108    ///
1109    /// `PostgreSQL` always validates, some other databases may not.
1110    pub validate_on_insert: bool,
1111}
1112
1113impl JsonDataType {
1114    /// Creates a new JSON type with binary storage format (JSONB in `PostgreSQL`).
1115    ///
1116    /// This is the recommended format for most use cases as it enables:
1117    /// - Faster query performance
1118    /// - GIN indexing support (`PostgreSQL`)
1119    /// - Normalized storage (no duplicate keys, sorted keys)
1120    ///
1121    /// # Example
1122    ///
1123    /// ```rust
1124    /// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1125    ///
1126    /// let json = JsonDataType::new();
1127    /// assert_eq!(json.storage_format, JsonStorageFormat::Binary);
1128    /// assert!(!json.validate_on_insert);
1129    /// ```
1130    #[must_use]
1131    pub fn new() -> Self {
1132        Self::default()
1133    }
1134
1135    /// Creates a JSON type with text storage format.
1136    ///
1137    /// Use this when you need to preserve:
1138    /// - Original whitespace formatting
1139    /// - Original key order
1140    /// - Duplicate keys (not recommended but sometimes required)
1141    ///
1142    /// # Example
1143    ///
1144    /// ```rust
1145    /// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1146    ///
1147    /// let json = JsonDataType::text();
1148    /// assert_eq!(json.storage_format, JsonStorageFormat::Text);
1149    /// assert!(json.validate_on_insert);
1150    /// ```
1151    #[must_use]
1152    pub fn text() -> Self {
1153        Self { storage_format: JsonStorageFormat::Text, validate_on_insert: true }
1154    }
1155
1156    /// Creates a JSON type with binary storage format.
1157    ///
1158    /// # Example
1159    ///
1160    /// ```rust
1161    /// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1162    ///
1163    /// let json = JsonDataType::binary();
1164    /// assert_eq!(json.storage_format, JsonStorageFormat::Binary);
1165    /// assert!(json.validate_on_insert);
1166    /// ```
1167    #[must_use]
1168    pub fn binary() -> Self {
1169        Self { storage_format: JsonStorageFormat::Binary, validate_on_insert: true }
1170    }
1171
1172    /// Sets whether to validate JSON on insert.
1173    ///
1174    /// # Example
1175    ///
1176    /// ```rust
1177    /// use razeri_plugin_contract::data_type::JsonDataType;
1178    ///
1179    /// let json = JsonDataType::new().with_validation(false);
1180    /// assert!(!json.validate_on_insert);
1181    /// ```
1182    #[must_use]
1183    pub fn with_validation(mut self, validate: bool) -> Self {
1184        self.validate_on_insert = validate;
1185        self
1186    }
1187}
1188
1189// ============================================================================
1190// Complex/Collection Types
1191// ============================================================================
1192
1193/// Array type containing elements of another type.
1194///
1195/// Supported by: `PostgreSQL`, `ClickHouse`, Snowflake (ARRAY), Databricks (ARRAY).
1196/// Not directly supported by SQL Server.
1197///
1198/// # Example
1199///
1200/// ```rust
1201/// use razeri_plugin_contract::{DataType, data_type::ArrayDataType};
1202///
1203/// let int_array = ArrayDataType::new(DataType::int());
1204/// assert_eq!(*int_array.element_type, DataType::int());
1205///
1206/// let _str_array = ArrayDataType::new(DataType::varchar(100));
1207/// ```
1208#[derive(Debug, Clone, PartialEq, Eq)]
1209pub struct ArrayDataType {
1210    /// The data type of array elements.
1211    pub element_type: Box<DataType>,
1212}
1213
1214impl ArrayDataType {
1215    /// Creates a new array type with the given element type.
1216    ///
1217    /// # Example
1218    ///
1219    /// ```rust
1220    /// use razeri_plugin_contract::{DataType, data_type::ArrayDataType};
1221    ///
1222    /// let arr = ArrayDataType::new(DataType::bigint());
1223    /// assert_eq!(*arr.element_type, DataType::bigint());
1224    /// ```
1225    #[must_use]
1226    pub fn new(element_type: DataType) -> Self {
1227        Self { element_type: Box::new(element_type) }
1228    }
1229}
1230
1231/// Map/Dictionary type with key-value pairs.
1232///
1233/// Supported by: `ClickHouse`, Snowflake (OBJECT), Databricks (MAP).
1234/// `PostgreSQL` uses JSONB, SQL Server uses JSON string.
1235///
1236/// # Example
1237///
1238/// ```rust
1239/// use razeri_plugin_contract::DataType;
1240/// use razeri_plugin_contract::data_type::MapDataType;
1241///
1242/// let map = MapDataType::new(DataType::varchar(50), DataType::int());
1243/// assert_eq!(*map.key_type, DataType::varchar(50));
1244/// assert_eq!(*map.value_type, DataType::int());
1245/// ```
1246#[derive(Debug, Clone, PartialEq, Eq)]
1247pub struct MapDataType {
1248    /// The data type of map keys.
1249    pub key_type: Box<DataType>,
1250
1251    /// The data type of map values.
1252    pub value_type: Box<DataType>,
1253}
1254
1255impl MapDataType {
1256    /// Creates a new map type with the given key and value types.
1257    ///
1258    /// # Example
1259    ///
1260    /// ```rust
1261    /// use razeri_plugin_contract::DataType;
1262    /// use razeri_plugin_contract::data_type::MapDataType;
1263    ///
1264    /// let map = MapDataType::new(DataType::varchar(50), DataType::int());
1265    /// assert_eq!(*map.key_type, DataType::varchar(50));
1266    /// assert_eq!(*map.value_type, DataType::int());
1267    /// ```
1268    #[must_use]
1269    pub fn new(key_type: DataType, value_type: DataType) -> Self {
1270        Self { key_type: Box::new(key_type), value_type: Box::new(value_type) }
1271    }
1272}
1273
1274// ============================================================================
1275// Unified Data Type Enum
1276// ============================================================================
1277
1278/// Unified data type enumeration covering all supported types across databases.
1279///
1280/// This is the **primary type system** for Razeri. Every field, column, and
1281/// parameter in a design document is typed using `DataType`. The enum captures
1282/// enough metadata to generate correct DDL for all 8 dialect plugins while
1283/// remaining abstract over platform-specific implementation details.
1284///
1285/// # Type Categories
1286///
1287/// | Category | Variants | Example |
1288/// |----------|----------|---------|
1289/// | Numeric | `Integer`, `Decimal`, `Float` | `INT`, `DECIMAL(19,4)`, `DOUBLE` |
1290/// | Boolean | `Boolean` | `BOOLEAN`, `BIT` |
1291/// | String | `Char`, `VarChar`, `Text` | `VARCHAR(100)`, `TEXT` |
1292/// | Binary | `Binary`, `VarBinary` | `BYTEA`, `VARBINARY` |
1293/// | Date/Time | `Date`, `Time`, `Timestamp`, `TimestampTz`, `DateTime`, `Interval` | `DATE`, `TIMESTAMP` |
1294/// | Special | `Uuid`, `Json` | `UUID`, `JSONB` |
1295/// | Collection | `Array`, `Map` | `INTEGER[]`, `MAP<TEXT,INT>` |
1296///
1297/// # Design Principles
1298///
1299/// 1. **Abstract over database-specific implementations** — `DataType` captures
1300///    enough metadata to generate correct DDL for each target, but does not
1301///    embed platform-specific syntax.
1302/// 2. **Preserve precision metadata** — Decimal types carry precision and scale;
1303///    string types carry length constraints; timestamp types carry fractional
1304///    seconds precision.
1305/// 3. **Support intersection of capabilities** — All types in the enum can be
1306///    rendered by all 8 dialect plugins, even if some platforms emulate a type
1307///    (e.g., JSON stored as `NVARCHAR(MAX)` in SQL Server).
1308///
1309/// # Usage
1310///
1311/// ```
1312/// use razeri_plugin_contract::DataType;
1313///
1314/// // Convenience constructors for common types
1315/// let id = DataType::bigint();
1316/// let name = DataType::varchar(255);
1317/// let price = DataType::decimal(19, 4);
1318/// let created = DataType::timestamp();
1319///
1320/// // JSON with binary storage (JSONB in PostgreSQL, VARIANT in Snowflake)
1321/// let data = DataType::json();
1322/// ```
1323///
1324/// # Cross-Database Mapping
1325///
1326/// Each variant maps to platform-specific types as documented in the variant
1327/// definitions. Dialect plugins resolve the final SQL type string during
1328/// rendering; the rendering traits live in this crate's `renderer` module.
1329#[derive(Debug, Clone, PartialEq, Eq)]
1330pub enum DataType {
1331    // Numeric types
1332    /// Signed/unsigned integer (1, 2, 4, or 8 bytes).
1333    Integer(IntegerDataType),
1334
1335    /// Exact numeric with fixed precision and scale.
1336    Decimal(DecimalDataType),
1337
1338    /// Approximate floating-point (4 or 8 bytes).
1339    Float(FloatDataType),
1340
1341    /// Boolean (true/false).
1342    Boolean(BooleanDataType),
1343
1344    // String types
1345    /// Fixed-length character string.
1346    Char(CharDataType),
1347
1348    /// Variable-length character string.
1349    VarChar(VarCharDataType),
1350
1351    /// Unbounded text type.
1352    Text(TextDataType),
1353
1354    // Binary types
1355    /// Fixed-length binary data.
1356    Binary(BinaryDataType),
1357
1358    /// Variable-length binary data.
1359    VarBinary(VarBinaryDataType),
1360
1361    // Date/Time types
1362    /// Date only (year, month, day).
1363    Date(DateDataType),
1364
1365    /// Time only (hour, minute, second, optional timezone).
1366    Time(TimeDataType),
1367
1368    /// Timestamp without timezone.
1369    Timestamp(TimestampDataType),
1370
1371    /// Timestamp with timezone.
1372    TimestampTz(TimestampTzDataType),
1373
1374    /// Combined date and time (legacy).
1375    DateTime(DateTimeDataType),
1376
1377    /// Interval/duration.
1378    Interval(IntervalDataType),
1379
1380    // Special types
1381    /// UUID/GUID.
1382    Uuid(UuidDataType),
1383
1384    /// JSON/semi-structured data.
1385    Json(JsonDataType),
1386
1387    // Complex types
1388    /// Array of elements.
1389    Array(ArrayDataType),
1390
1391    /// Map/dictionary with key-value pairs.
1392    Map(MapDataType),
1393}
1394
1395impl DataType {
1396    // ==========================================================================
1397    // Numeric Type Constructors
1398    // ==========================================================================
1399
1400    /// Creates a 1-byte signed integer (`TINYINT` / `SMALLINT` / `Int8`).
1401    ///
1402    /// # Example
1403    ///
1404    /// ```
1405    /// use razeri_plugin_contract::DataType;
1406    ///
1407    /// let ty = DataType::tinyint();
1408    /// assert!(matches!(ty, DataType::Integer(_)));
1409    /// ```
1410    #[must_use]
1411    pub fn tinyint() -> Self {
1412        DataType::Integer(IntegerDataType::new(IntegerSize::Tiny))
1413    }
1414
1415    /// Creates a 2-byte signed integer (`SMALLINT` / `Int16`).
1416    ///
1417    /// # Example
1418    ///
1419    /// ```
1420    /// use razeri_plugin_contract::DataType;
1421    ///
1422    /// let ty = DataType::smallint();
1423    /// assert!(matches!(ty, DataType::Integer(_)));
1424    /// ```
1425    #[must_use]
1426    pub fn smallint() -> Self {
1427        DataType::Integer(IntegerDataType::new(IntegerSize::Small))
1428    }
1429
1430    /// Creates a 4-byte signed integer (`INT` / `INTEGER` / `Int32`).
1431    ///
1432    /// # Example
1433    ///
1434    /// ```
1435    /// use razeri_plugin_contract::DataType;
1436    ///
1437    /// let id = DataType::int();
1438    /// assert!(matches!(id, DataType::Integer(_)));
1439    /// ```
1440    #[must_use]
1441    pub fn int() -> Self {
1442        DataType::Integer(IntegerDataType::new(IntegerSize::Standard))
1443    }
1444
1445    /// Creates an 8-byte signed integer (`BIGINT` / `Int64`).
1446    ///
1447    /// # Example
1448    ///
1449    /// ```
1450    /// use razeri_plugin_contract::DataType;
1451    ///
1452    /// let id = DataType::bigint();
1453    /// assert!(matches!(id, DataType::Integer(_)));
1454    /// ```
1455    #[must_use]
1456    pub fn bigint() -> Self {
1457        DataType::Integer(IntegerDataType::new(IntegerSize::Big))
1458    }
1459
1460    /// Creates a `DECIMAL(precision, scale)` type.
1461    ///
1462    /// # Panics
1463    ///
1464    /// Panics if `precision` is 0 or exceeds 38, or if `scale > precision`.
1465    ///
1466    /// # Example
1467    ///
1468    /// ```
1469    /// use razeri_plugin_contract::DataType;
1470    ///
1471    /// let price = DataType::decimal(19, 4);
1472    /// assert!(matches!(price, DataType::Decimal(_)));
1473    /// ```
1474    ///
1475    /// ```should_panic
1476    /// use razeri_plugin_contract::DataType;
1477    ///
1478    /// let _ = DataType::decimal(0, 0); // panics: precision must be 1-38
1479    /// ```
1480    #[must_use]
1481    pub fn decimal(precision: u8, scale: u8) -> Self {
1482        DataType::Decimal(DecimalDataType::new(precision, scale))
1483    }
1484
1485    /// Creates a single-precision float (`REAL` / `FLOAT4`).
1486    ///
1487    /// # Example
1488    ///
1489    /// ```
1490    /// use razeri_plugin_contract::DataType;
1491    ///
1492    /// let ty = DataType::float();
1493    /// assert!(matches!(ty, DataType::Float(_)));
1494    /// ```
1495    #[must_use]
1496    pub fn float() -> Self {
1497        DataType::Float(FloatDataType::single())
1498    }
1499
1500    /// Creates a double-precision float (`DOUBLE PRECISION` / `DOUBLE`).
1501    ///
1502    /// # Example
1503    ///
1504    /// ```
1505    /// use razeri_plugin_contract::DataType;
1506    ///
1507    /// let ty = DataType::double();
1508    /// assert!(matches!(ty, DataType::Float(_)));
1509    /// ```
1510    #[must_use]
1511    pub fn double() -> Self {
1512        DataType::Float(FloatDataType::double())
1513    }
1514
1515    // ==========================================================================
1516    // Boolean Type Constructor
1517    // ==========================================================================
1518
1519    /// Creates a boolean type (`BOOLEAN` / `BIT`).
1520    ///
1521    /// # Example
1522    ///
1523    /// ```
1524    /// use razeri_plugin_contract::DataType;
1525    ///
1526    /// let ty = DataType::boolean();
1527    /// assert!(matches!(ty, DataType::Boolean(_)));
1528    /// ```
1529    #[must_use]
1530    pub fn boolean() -> Self {
1531        DataType::Boolean(BooleanDataType)
1532    }
1533
1534    // ==========================================================================
1535    // String Type Constructors
1536    // ==========================================================================
1537
1538    /// Creates a `VARCHAR(max_length)` type.
1539    ///
1540    /// # Example
1541    ///
1542    /// ```
1543    /// use razeri_plugin_contract::DataType;
1544    ///
1545    /// let name = DataType::varchar(255);
1546    /// assert!(matches!(name, DataType::VarChar(_)));
1547    /// ```
1548    #[must_use]
1549    pub fn varchar(max_length: u32) -> Self {
1550        DataType::VarChar(VarCharDataType::new(max_length))
1551    }
1552
1553    /// Creates an unbounded text type (`TEXT` / `VARCHAR(MAX)` / `STRING`).
1554    ///
1555    /// # Example
1556    ///
1557    /// ```
1558    /// use razeri_plugin_contract::DataType;
1559    ///
1560    /// let ty = DataType::text();
1561    /// assert!(matches!(ty, DataType::Text(_)));
1562    /// ```
1563    #[must_use]
1564    pub fn text() -> Self {
1565        DataType::Text(TextDataType)
1566    }
1567
1568    // ==========================================================================
1569    // Date/Time Type Constructors
1570    // ==========================================================================
1571
1572    /// Creates a date-only type (`DATE`).
1573    ///
1574    /// # Example
1575    ///
1576    /// ```
1577    /// use razeri_plugin_contract::DataType;
1578    ///
1579    /// let ty = DataType::date();
1580    /// assert!(matches!(ty, DataType::Date(_)));
1581    /// ```
1582    #[must_use]
1583    pub fn date() -> Self {
1584        DataType::Date(DateDataType)
1585    }
1586
1587    /// Creates a timestamp without timezone (`TIMESTAMP`).
1588    ///
1589    /// # Example
1590    ///
1591    /// ```
1592    /// use razeri_plugin_contract::DataType;
1593    ///
1594    /// let created = DataType::timestamp();
1595    /// assert!(matches!(created, DataType::Timestamp(_)));
1596    /// ```
1597    #[must_use]
1598    pub fn timestamp() -> Self {
1599        DataType::Timestamp(TimestampDataType::default())
1600    }
1601
1602    /// Creates a timestamp with timezone (`TIMESTAMPTZ` / `DATETIMEOFFSET`).
1603    ///
1604    /// # Example
1605    ///
1606    /// ```
1607    /// use razeri_plugin_contract::DataType;
1608    ///
1609    /// let created_tz = DataType::timestamp_tz();
1610    /// assert!(matches!(created_tz, DataType::TimestampTz(_)));
1611    /// ```
1612    #[must_use]
1613    pub fn timestamp_tz() -> Self {
1614        DataType::TimestampTz(TimestampTzDataType::default())
1615    }
1616
1617    // ==========================================================================
1618    // Special Type Constructors
1619    // ==========================================================================
1620
1621    /// Creates a UUID type (`UUID` / `UNIQUEIDENTIFIER`).
1622    ///
1623    /// # Example
1624    ///
1625    /// ```
1626    /// use razeri_plugin_contract::DataType;
1627    ///
1628    /// let ty = DataType::uuid();
1629    /// assert!(matches!(ty, DataType::Uuid(_)));
1630    /// ```
1631    #[must_use]
1632    pub fn uuid() -> Self {
1633        DataType::Uuid(UuidDataType)
1634    }
1635
1636    /// Creates a JSON type with binary storage (JSONB in `PostgreSQL`, VARIANT in Snowflake).
1637    ///
1638    /// # Example
1639    ///
1640    /// ```
1641    /// use razeri_plugin_contract::DataType;
1642    ///
1643    /// let data = DataType::json();
1644    /// assert!(matches!(data, DataType::Json(_)));
1645    /// ```
1646    #[must_use]
1647    pub fn json() -> Self {
1648        DataType::Json(JsonDataType::binary())
1649    }
1650
1651    /// Creates a JSON type with text storage (preserves formatting).
1652    ///
1653    /// # Example
1654    ///
1655    /// ```
1656    /// use razeri_plugin_contract::DataType;
1657    ///
1658    /// let data = DataType::json_text();
1659    /// assert!(matches!(data, DataType::Json(_)));
1660    /// ```
1661    #[must_use]
1662    pub fn json_text() -> Self {
1663        DataType::Json(JsonDataType::text())
1664    }
1665}
1666
1667// ============================================================================
1668// Tests
1669// ============================================================================
1670
1671#[cfg(test)]
1672mod tests {
1673    use super::*;
1674
1675    #[test]
1676    fn test_integer_types() {
1677        let tiny = IntegerDataType::new(IntegerSize::Tiny);
1678        assert_eq!(tiny.byte_size(), 1);
1679        assert!(!tiny.unsigned);
1680
1681        let big_unsigned = IntegerDataType::unsigned(IntegerSize::Big);
1682        assert_eq!(big_unsigned.byte_size(), 8);
1683        assert!(big_unsigned.unsigned);
1684    }
1685
1686    #[test]
1687    fn test_decimal_type() {
1688        let money = DecimalDataType::money();
1689        assert_eq!(money.precision, 19);
1690        assert_eq!(money.scale, 4);
1691    }
1692
1693    #[test]
1694    #[should_panic(expected = "Scale cannot exceed precision")]
1695    fn test_decimal_invalid_scale() {
1696        let _ = DecimalDataType::new(5, 10);
1697    }
1698
1699    #[test]
1700    fn test_convenience_constructors() {
1701        assert!(matches!(DataType::int(), DataType::Integer(_)));
1702        assert!(matches!(DataType::varchar(100), DataType::VarChar(_)));
1703        assert!(matches!(DataType::timestamp(), DataType::Timestamp(_)));
1704    }
1705}
1706
1707// ============================================================================
1708// Simplified Data Type Reference (for CAST expressions)
1709// ============================================================================
1710
1711/// Simplified data type reference used in CAST / `TRY_CAST` / CONVERT expressions.
1712///
1713/// Unlike [`DataType`], which captures full type metadata for DDL generation,
1714/// `DataTypeRef` represents the type names commonly used in SQL expressions.
1715///
1716/// # Purpose
1717///
1718/// `DataTypeRef` is a lightweight, platform-agnostic type reference suitable for
1719/// embedding in SQL expressions such as:
1720///
1721/// ```sql
1722/// SELECT CAST(column_name AS VARCHAR(100)) FROM table
1723/// SELECT TRY_CAST(value AS DECIMAL(19, 4)) FROM source
1724/// SELECT CONVERT(DATETIME2, timestamp_col) FROM events
1725/// ```
1726///
1727/// # Differences from `DataType`
1728///
1729/// | Aspect | `DataType` | `DataTypeRef` |
1730/// |--------|-----------|---------------|
1731/// | **Use case** | DDL generation | CAST / CONVERT expressions |
1732/// | **Metadata** | Full (encoding, precision, storage format) | Minimal (precision, scale, length) |
1733/// | **Platform variants** | Unified (one variant per type) | Separate (`Decimal` vs `Numeric`, `DateTime` vs `DateTime2`) |
1734/// | **Collections** | Array, Map | Not supported |
1735///
1736/// # Platform-Specific Variants
1737///
1738/// Some types have separate variants for platform-specific naming:
1739///
1740/// - `Decimal` vs `Numeric` — equivalent in SQL, but some dialects prefer one over the other
1741/// - `DateTime` vs `DateTime2` — SQL Server uses `DATETIME2` for precision
1742/// - `Json` vs `Jsonb` — `PostgreSQL` distinguishes between text and binary JSON
1743/// - `Boolean` vs `Bit` — SQL Server uses `BIT` for boolean
1744///
1745/// # Example
1746///
1747/// ```
1748/// use razeri_plugin_contract::DataTypeRef;
1749///
1750/// let varchar = DataTypeRef::Varchar { length: Some(100) };
1751/// let decimal = DataTypeRef::Decimal { precision: 19, scale: 4 };
1752/// let timestamp = DataTypeRef::Timestamp { precision: Some(6) };
1753/// ```
1754#[derive(Debug, Clone, PartialEq)]
1755pub enum DataTypeRef {
1756    // ==========================================================================
1757    // Numeric Types
1758    // ==========================================================================
1759    /// 4-byte signed integer (`INT` / `INTEGER`).
1760    Int,
1761
1762    /// 8-byte signed integer (`BIGINT`).
1763    BigInt,
1764
1765    /// 2-byte signed integer (`SMALLINT`).
1766    SmallInt,
1767
1768    /// 1-byte signed integer (`TINYINT` / `SMALLINT` in `PostgreSQL`).
1769    TinyInt,
1770
1771    /// Single-precision float (`REAL` / `FLOAT4`).
1772    Float,
1773
1774    /// Double-precision float (`DOUBLE PRECISION` / `DOUBLE`).
1775    Double,
1776
1777    /// Single-precision float (`REAL` / `FLOAT4`).
1778    Real,
1779
1780    /// Exact numeric with precision and scale (`DECIMAL(p, s)`).
1781    Decimal {
1782        /// Total number of digits.
1783        precision: u8,
1784        /// Number of digits after the decimal point.
1785        scale: u8,
1786    },
1787
1788    /// Exact numeric with precision and scale (`NUMERIC(p, s)`).
1789    ///
1790    /// Equivalent to `Decimal` in SQL, but some dialects prefer `NUMERIC`
1791    /// over `DECIMAL` for exact numeric types.
1792    Numeric {
1793        /// Total number of digits.
1794        precision: u8,
1795        /// Number of digits after the decimal point.
1796        scale: u8,
1797    },
1798
1799    // ==========================================================================
1800    // String Types
1801    // ==========================================================================
1802    /// Variable-length character string (`VARCHAR(n)`).
1803    Varchar {
1804        /// Maximum length in characters (None = unlimited/MAX).
1805        length: Option<u16>,
1806    },
1807
1808    /// Fixed-length character string (`CHAR(n)`).
1809    Char {
1810        /// Length in characters.
1811        length: u16,
1812    },
1813
1814    /// Unbounded text type (`TEXT`).
1815    Text,
1816
1817    /// Variable-length Unicode string (`NVARCHAR(n)` — SQL Server).
1818    NVarchar {
1819        /// Maximum length in characters (None = unlimited/MAX).
1820        length: Option<u16>,
1821    },
1822
1823    /// Fixed-length Unicode string (`NCHAR(n)` — SQL Server).
1824    NChar {
1825        /// Length in characters.
1826        length: u16,
1827    },
1828
1829    // ==========================================================================
1830    // Binary Types
1831    // ==========================================================================
1832    /// Fixed-length binary data (`BINARY(n)`).
1833    Binary {
1834        /// Length in bytes.
1835        length: Option<u16>,
1836    },
1837
1838    /// Variable-length binary data (`VARBINARY(n)`).
1839    VarBinary {
1840        /// Maximum length in bytes (None = unlimited/MAX).
1841        length: Option<u16>,
1842    },
1843
1844    /// Unbounded binary data (`BLOB` / `BYTEA`).
1845    Blob,
1846
1847    // ==========================================================================
1848    // Date/Time Types
1849    // ==========================================================================
1850    /// Date only (`DATE`).
1851    Date,
1852
1853    /// Time with optional precision (`TIME(p)`).
1854    Time {
1855        /// Fractional seconds precision (None = default).
1856        precision: Option<u8>,
1857    },
1858
1859    /// Combined date and time (`DATETIME` — SQL Server legacy).
1860    DateTime,
1861
1862    /// Combined date and time with precision (`DATETIME2(p)` — SQL Server).
1863    DateTime2 {
1864        /// Fractional seconds precision (None = default).
1865        precision: Option<u8>,
1866    },
1867
1868    /// Timestamp with optional precision (`TIMESTAMP(p)`).
1869    Timestamp {
1870        /// Fractional seconds precision (None = default).
1871        precision: Option<u8>,
1872    },
1873
1874    /// Timestamp with timezone and optional precision (`TIMESTAMPTZ(p)`).
1875    TimestampTz {
1876        /// Fractional seconds precision (None = default).
1877        precision: Option<u8>,
1878    },
1879
1880    /// Interval/duration (`INTERVAL`).
1881    Interval,
1882
1883    // ==========================================================================
1884    // Boolean Types
1885    // ==========================================================================
1886    /// Boolean (`BOOLEAN` — `PostgreSQL`, Snowflake, `ClickHouse`).
1887    Boolean,
1888
1889    /// Bit (`BIT` — SQL Server).
1890    Bit,
1891
1892    // ==========================================================================
1893    // JSON/Semi-structured Types
1894    // ==========================================================================
1895    /// Text-based JSON (`JSON` — `PostgreSQL`).
1896    Json,
1897
1898    /// Binary-optimized JSON (`JSONB` — `PostgreSQL`).
1899    Jsonb,
1900
1901    /// Semi-structured variant (`VARIANT` — Snowflake).
1902    Variant,
1903
1904    // ==========================================================================
1905    // Other Types
1906    // ==========================================================================
1907    /// UUID/GUID (`UUID` / `UNIQUEIDENTIFIER`).
1908    Uuid,
1909
1910    /// XML data (`XML`).
1911    Xml,
1912
1913    /// Custom type name (for types not covered by the enum variants).
1914    Custom(String),
1915}