Skip to main content

razeri_plugin_contract/
data_type.rs

1// Copyright (C) 2026 Gabriel Shea <gshea@ideapivot.com> - All Rights Reserved
2
3//! Generic data types for cross-database compatibility.
4//!
5//! This module defines the unified [`DataType`] enum and all component types
6//! (integer, decimal, float, boolean, string, binary, date/time, special, and
7//! collection types) that serve as the foundation for SQL rendering across
8//! all supported database platforms.
9//!
10//! # Design Philosophy
11//!
12//! - **Abstract over database-specific implementations** — `DataType` captures
13//!   enough metadata to generate correct DDL for each target, but does not
14//!   embed platform-specific syntax.
15//! - **Preserve precision metadata** — Decimal types carry precision and scale;
16//!   string types carry length constraints; timestamp types carry fractional
17//!   seconds precision.
18//! - **Support intersection of capabilities** — All types in the enum can be
19//!   rendered by all 8 dialect plugins, even if some platforms emulate a type
20//!   (e.g., JSON stored as `NVARCHAR(MAX)` in SQL Server).
21//!
22//! # Type Categories
23//!
24//! | Category | Types | Example |
25//! |----------|-------|---------|
26//! | Integer | [`IntegerDataType`] | `INT`, `BIGINT`, `TINYINT` |
27//! | Decimal | [`DecimalDataType`] | `DECIMAL(19,4)`, `DECIMAL(5,2)` |
28//! | Float | [`FloatDataType`] | `REAL`, `DOUBLE` |
29//! | Boolean | [`BooleanDataType`] | `BOOLEAN`, `BIT` |
30//! | String | [`CharDataType`], [`VarCharDataType`], [`TextDataType`] | `VARCHAR(100)`, `TEXT` |
31//! | Binary | [`BinaryDataType`], [`VarBinaryDataType`] | `BYTEA`, `VARBINARY` |
32//! | Date/Time | [`DateDataType`], [`TimestampDataType`], [`TimestampTzDataType`] | `DATE`, `TIMESTAMP` |
33//! | Special | [`UuidDataType`], [`JsonDataType`] | `UUID`, `JSONB` |
34//! | Collection | [`ArrayDataType`], [`MapDataType`] | `INTEGER[]`, `MAP<TEXT,INT>` |
35//!
36//! # Usage
37//!
38//! Convenience constructors for common types:
39//!
40//! ```rust
41//! use razeri_plugin_contract::DataType;
42//!
43//! let id = DataType::bigint();
44//! let name = DataType::varchar(255);
45//! let price = DataType::decimal(19, 4);
46//! let created = DataType::timestamp();
47//!
48//! // JSON with binary storage (JSONB in PostgreSQL, VARIANT in Snowflake)
49//! let data = DataType::json();
50//! ```
51//!
52//! Each variant maps to platform-specific types as documented in the variant
53//! definitions. Dialect plugins resolve the final SQL type string during
54//! rendering. See `razeri-render-api` for
55//! the rendering trait definitions.
56
57// ============================================================================
58// Integer Types
59// ============================================================================
60
61/// Integer size variants matching common database integer types.
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63pub enum IntegerSize {
64    /// 1 byte: -128 to 127 (signed) or 0 to 255 (unsigned)
65    /// Maps to: TINYINT (SQL Server, Snowflake, Databricks), SMALLINT (`PostgreSQL`), Int8/UInt8 (`ClickHouse`)
66    Tiny,
67    /// 2 bytes: -32,768 to 32,767 (signed)
68    /// Maps to: SMALLINT (all databases), Int16/UInt16 (`ClickHouse`)
69    Small,
70    /// 4 bytes: -2.1B to 2.1B (signed)
71    /// Maps to: INT/INTEGER (all databases), Int32/UInt32 (`ClickHouse`)
72    Standard,
73    /// 8 bytes: -9.2E18 to 9.2E18 (signed)
74    /// Maps to: BIGINT (all databases), Int64/UInt64 (`ClickHouse`)
75    Big,
76}
77
78/// Integer data type supporting signed/unsigned variants.
79///
80/// Most databases only support signed integers. `ClickHouse` is the exception,
81/// supporting unsigned variants via `UInt8`, `UInt16`, `UInt32`, `UInt64`.
82/// Other dialect plugins will map unsigned types to the next larger signed type
83/// when unsigned is not supported.
84///
85/// # Example
86///
87/// ```rust
88/// use razeri_plugin_contract::{DataType, data_type::{IntegerDataType, IntegerSize}};
89///
90/// // Signed 4-byte integer (INT/INTEGER on most platforms)
91/// let _id = DataType::int();
92///
93/// // Unsigned 8-byte integer (UInt64 in ClickHouse, BIGINT elsewhere)
94/// let big = IntegerDataType::unsigned(IntegerSize::Big);
95/// assert_eq!(big.byte_size(), 8);
96/// ```
97#[derive(Debug, Clone, PartialEq, Eq, Hash)]
98pub struct IntegerDataType {
99    /// Size of the integer (Tiny, Small, Standard, Big).
100    pub size: IntegerSize,
101
102    /// Whether the integer is unsigned.
103    ///
104    /// Only `ClickHouse` fully supports unsigned integers. Other dialect plugins
105    /// will map unsigned types to the next larger signed type when unsigned is
106    /// not supported by the target platform.
107    ///
108    /// # Example
109    ///
110    /// ```rust
111    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
112    ///
113    /// let signed = IntegerDataType::new(IntegerSize::Standard);
114    /// assert!(!signed.unsigned);
115    ///
116    /// let unsigned = IntegerDataType::unsigned(IntegerSize::Big);
117    /// assert!(unsigned.unsigned);
118    /// ```
119    pub unsigned: bool,
120}
121
122impl IntegerDataType {
123    /// Creates a new signed integer with the given [`IntegerSize`].
124    ///
125    /// # Example
126    ///
127    /// ```rust
128    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
129    ///
130    /// let small = IntegerDataType::new(IntegerSize::Small);
131    /// assert_eq!(small.byte_size(), 2);
132    /// ```
133    #[must_use]
134    pub fn new(size: IntegerSize) -> Self {
135        Self { size, unsigned: false }
136    }
137
138    /// Creates a new unsigned integer with the given [`IntegerSize`].
139    ///
140    /// # Example
141    ///
142    /// ```rust
143    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
144    ///
145    /// let unsigned_big = IntegerDataType::unsigned(IntegerSize::Big);
146    /// assert!(unsigned_big.unsigned);
147    /// assert_eq!(unsigned_big.byte_size(), 8);
148    /// ```
149    #[must_use]
150    pub fn unsigned(size: IntegerSize) -> Self {
151        Self { size, unsigned: true }
152    }
153
154    /// Returns the byte size of this integer type.
155    ///
156    /// # Example
157    ///
158    /// ```rust
159    /// use razeri_plugin_contract::data_type::{IntegerDataType, IntegerSize};
160    ///
161    /// assert_eq!(IntegerDataType::new(IntegerSize::Tiny).byte_size(), 1);
162    /// assert_eq!(IntegerDataType::new(IntegerSize::Small).byte_size(), 2);
163    /// assert_eq!(IntegerDataType::new(IntegerSize::Standard).byte_size(), 4);
164    /// assert_eq!(IntegerDataType::new(IntegerSize::Big).byte_size(), 8);
165    /// ```
166    #[must_use]
167    pub fn byte_size(&self) -> usize {
168        match self.size {
169            IntegerSize::Tiny => 1,
170            IntegerSize::Small => 2,
171            IntegerSize::Standard => 4,
172            IntegerSize::Big => 8,
173        }
174    }
175}
176
177// ============================================================================
178// Decimal/Numeric Types (Exact Precision)
179// ============================================================================
180
181/// Exact numeric type with fixed precision and scale.
182///
183/// Maps to: `NUMERIC`/`DECIMAL` (`PostgreSQL`, SQL Server), `NUMBER` (Snowflake),
184/// `DECIMAL` (Databricks), `Decimal` (`ClickHouse`).
185///
186/// # Constraints
187///
188/// - `precision`: 1–38 (most databases), up to 76 (some platforms)
189/// - `scale`: 0 to `precision`
190/// - Panics if `scale > precision` or `precision > 38`
191///
192/// # Example
193///
194/// ```rust
195/// use razeri_plugin_contract::data_type::DecimalDataType;
196///
197/// // Money: DECIMAL(19, 4)
198/// let money = DecimalDataType::money();
199/// assert_eq!(money.precision, 19);
200/// assert_eq!(money.scale, 4);
201///
202/// // Percentage: DECIMAL(5, 2)
203/// let pct = DecimalDataType::percentage();
204/// assert_eq!(pct.precision, 5);
205/// assert_eq!(pct.scale, 2);
206///
207/// // Custom decimal
208/// let custom = DecimalDataType::new(10, 2);
209/// assert_eq!(custom.precision, 10);
210/// assert_eq!(custom.scale, 2);
211/// ```
212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
213pub struct DecimalDataType {
214    /// Total number of digits (1–38 for most databases, up to 76 for some).
215    pub precision: u8,
216
217    /// Number of digits after the decimal point (0 to `precision`).
218    pub scale: u8,
219}
220
221impl DecimalDataType {
222    /// Creates a new decimal type with the given precision and scale.
223    ///
224    /// # Panics
225    ///
226    /// Panics if `precision` is 0 or exceeds 38, or if `scale > precision`.
227    ///
228    /// # Example
229    ///
230    /// ```rust
231    /// use razeri_plugin_contract::data_type::DecimalDataType;
232    ///
233    /// let dec = DecimalDataType::new(10, 2);
234    /// assert_eq!(dec.precision, 10);
235    /// assert_eq!(dec.scale, 2);
236    /// ```
237    ///
238    /// # Panics
239    ///
240    /// ```should_panic
241    /// use razeri_plugin_contract::data_type::DecimalDataType;
242    ///
243    /// // Precision must be 1-38
244    /// let _ = DecimalDataType::new(0, 0); // panics
245    ///
246    /// // Scale cannot exceed precision
247    /// let _ = DecimalDataType::new(5, 10); // panics
248    /// ```
249    #[must_use]
250    pub fn new(precision: u8, scale: u8) -> Self {
251        assert!(precision > 0 && precision <= 38, "Precision must be 1-38");
252        assert!(scale <= precision, "Scale cannot exceed precision");
253        Self { precision, scale }
254    }
255
256    /// Creates the common money type: `DECIMAL(19, 4)`.
257    ///
258    /// # Example
259    ///
260    /// ```rust
261    /// use razeri_plugin_contract::data_type::DecimalDataType;
262    ///
263    /// let money = DecimalDataType::money();
264    /// assert_eq!(money.precision, 19);
265    /// assert_eq!(money.scale, 4);
266    /// ```
267    #[must_use]
268    pub fn money() -> Self {
269        Self { precision: 19, scale: 4 }
270    }
271
272    /// Creates a percentage type with 2 decimal places: `DECIMAL(5, 2)`.
273    ///
274    /// # Example
275    ///
276    /// ```rust
277    /// use razeri_plugin_contract::data_type::DecimalDataType;
278    ///
279    /// let pct = DecimalDataType::percentage();
280    /// assert_eq!(pct.precision, 5);
281    /// assert_eq!(pct.scale, 2);
282    /// ```
283    #[must_use]
284    pub fn percentage() -> Self {
285        Self { precision: 5, scale: 2 }
286    }
287}
288
289// ============================================================================
290// Floating Point Types (Approximate)
291// ============================================================================
292
293/// Floating point precision variants.
294///
295/// # Example
296///
297/// ```rust
298/// use razeri_plugin_contract::data_type::FloatPrecision;
299///
300/// let single = FloatPrecision::Single;
301/// let double = FloatPrecision::Double;
302///
303/// assert_eq!(format!("{:?}", single), "Single");
304/// assert_eq!(format!("{:?}", double), "Double");
305/// ```
306#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
307pub enum FloatPrecision {
308    /// Single precision (4 bytes, ~7 decimal digits).
309    ///
310    /// Maps to: `REAL`/`FLOAT4` (`PostgreSQL`), `REAL`/`FLOAT(24)` (SQL Server),
311    /// `FLOAT` (Snowflake, Databricks), `Float32` (`ClickHouse`).
312    Single,
313    /// Double precision (8 bytes, ~15 decimal digits).
314    ///
315    /// Maps to: `DOUBLE PRECISION`/`FLOAT8` (`PostgreSQL`), `FLOAT` (SQL Server),
316    /// `DOUBLE` (Snowflake, Databricks), `Float64` (`ClickHouse`).
317    Double,
318}
319
320/// Approximate floating-point numeric type.
321///
322/// Maps to: `REAL`/`DOUBLE PRECISION` (`PostgreSQL`), `REAL`/`FLOAT` (SQL Server),
323/// `FLOAT`/`DOUBLE` (Snowflake, Databricks), `Float32`/`Float64` (`ClickHouse`).
324///
325/// # Example
326///
327/// ```
328/// use razeri_plugin_contract::DataType;
329///
330/// // Single precision (REAL)
331/// let real = DataType::float();
332///
333/// // Double precision (DOUBLE)
334/// let double = DataType::double();
335/// ```
336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
337pub struct FloatDataType {
338    /// Precision level (Single or Double).
339    pub precision: FloatPrecision,
340}
341
342impl FloatDataType {
343    /// Creates a single-precision (4-byte) float type.
344    ///
345    /// # Example
346    ///
347    /// ```rust
348    /// use razeri_plugin_contract::data_type::{FloatDataType, FloatPrecision};
349    ///
350    /// let single = FloatDataType::single();
351    /// assert_eq!(single.precision, FloatPrecision::Single);
352    /// ```
353    #[must_use]
354    pub fn single() -> Self {
355        Self { precision: FloatPrecision::Single }
356    }
357
358    /// Creates a double-precision (8-byte) float type.
359    ///
360    /// # Example
361    ///
362    /// ```rust
363    /// use razeri_plugin_contract::data_type::{FloatDataType, FloatPrecision};
364    ///
365    /// let double = FloatDataType::double();
366    /// assert_eq!(double.precision, FloatPrecision::Double);
367    /// ```
368    #[must_use]
369    pub fn double() -> Self {
370        Self { precision: FloatPrecision::Double }
371    }
372}
373
374// ============================================================================
375// Boolean Type
376// ============================================================================
377
378/// Boolean data type (true/false).
379///
380/// Maps to: `BOOLEAN` (`PostgreSQL`, Snowflake, Databricks, `ClickHouse`),
381/// `BIT` (SQL Server), `Bool`/`UInt8` (`ClickHouse`).
382///
383/// # Example
384///
385/// ```
386/// use razeri_plugin_contract::DataType;
387///
388/// let bool_type = DataType::boolean();
389/// ```
390#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
391pub struct BooleanDataType;
392
393// ============================================================================
394// String/Text Types
395// ============================================================================
396
397/// Character encoding for string types.
398///
399/// Determines whether strings use ASCII/Latin1 (1 byte per character) or
400/// UTF-8/Unicode (variable bytes per character). The default is `Unicode`.
401///
402/// # Example
403///
404/// ```rust
405/// use razeri_plugin_contract::data_type::StringEncoding;
406///
407/// let ascii = StringEncoding::Ascii;
408/// let unicode = StringEncoding::Unicode;
409///
410/// assert_eq!(ascii, StringEncoding::Ascii);
411/// assert_eq!(unicode, StringEncoding::Unicode);
412/// ```
413#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
414pub enum StringEncoding {
415    /// ASCII/Latin1 encoding (1 byte per character).
416    ///
417    /// Maps to: `CHAR`/`VARCHAR` (SQL Server), `CHAR`/`VARCHAR` (`PostgreSQL`).
418    Ascii,
419    /// UTF-8/Unicode encoding (variable bytes per character).
420    ///
421    /// Maps to: `NCHAR`/`NVARCHAR` (SQL Server), all strings in
422    /// `PostgreSQL`/Snowflake/`ClickHouse`.
423    #[default]
424    Unicode,
425}
426
427/// Fixed-length character string.
428///
429/// Maps to: `CHAR(n)` (`PostgreSQL`, SQL Server, Snowflake), `FixedString(n)` (`ClickHouse`).
430/// Note: Databricks does not have a fixed-length char type.
431///
432/// # Example
433///
434/// ```rust
435/// use razeri_plugin_contract::data_type::{CharDataType, StringEncoding};
436///
437/// // Default: Unicode encoding
438/// let char_type = CharDataType::new(50);
439/// assert_eq!(char_type.length, 50);
440///
441/// // Explicit ASCII encoding
442/// let ascii_char = CharDataType::with_encoding(10, StringEncoding::Ascii);
443/// ```
444#[derive(Debug, Clone, PartialEq, Eq, Hash)]
445pub struct CharDataType {
446    /// Fixed length in characters (1-8000 for SQL Server, 1-16777216 for Snowflake).
447    pub length: u32,
448
449    /// Character encoding (default: Unicode).
450    pub encoding: StringEncoding,
451}
452
453impl CharDataType {
454    /// Creates a fixed-length character string with Unicode encoding.
455    ///
456    /// # Example
457    ///
458    /// ```rust
459    /// use razeri_plugin_contract::data_type::CharDataType;
460    ///
461    /// let char = CharDataType::new(100);
462    /// assert_eq!(char.length, 100);
463    /// ```
464    #[must_use]
465    pub fn new(length: u32) -> Self {
466        Self { length, encoding: StringEncoding::Unicode }
467    }
468
469    /// Creates a fixed-length character string with the specified encoding.
470    ///
471    /// # Example
472    ///
473    /// ```rust
474    /// use razeri_plugin_contract::data_type::{CharDataType, StringEncoding};
475    ///
476    /// let ascii = CharDataType::with_encoding(20, StringEncoding::Ascii);
477    /// assert_eq!(ascii.encoding, StringEncoding::Ascii);
478    /// ```
479    #[must_use]
480    pub fn with_encoding(length: u32, encoding: StringEncoding) -> Self {
481        Self { length, encoding }
482    }
483}
484
485/// Variable-length character string with optional max length.
486///
487/// Maps to: `VARCHAR(n)` (`PostgreSQL`, SQL Server, Snowflake), `STRING` (Databricks, `ClickHouse`).
488///
489/// # Example
490///
491/// ```rust
492/// use razeri_plugin_contract::data_type::{VarCharDataType, StringEncoding};
493///
494/// // Fixed max length
495/// let varchar = VarCharDataType::new(255);
496/// assert_eq!(varchar.max_length, Some(255));
497///
498/// // Unlimited length (TEXT/VARCHAR(MAX)/STRING)
499/// let text = VarCharDataType::unlimited();
500/// assert_eq!(text.max_length, None);
501///
502/// // Explicit ASCII encoding
503/// let ascii = VarCharDataType::with_encoding(Some(100), StringEncoding::Ascii);
504/// ```
505#[derive(Debug, Clone, PartialEq, Eq, Hash)]
506pub struct VarCharDataType {
507    /// Maximum length in characters (None = unlimited/MAX).
508    ///
509    /// Platform limits:
510    /// - `PostgreSQL`: up to 10,485,760
511    /// - SQL Server: up to 8,000 (or MAX)
512    /// - Snowflake: up to 16,777,216
513    /// - `ClickHouse`: unlimited
514    pub max_length: Option<u32>,
515
516    /// Character encoding (default: Unicode).
517    pub encoding: StringEncoding,
518}
519
520impl VarCharDataType {
521    /// Creates a VARCHAR with specified max length.
522    ///
523    /// # Example
524    ///
525    /// ```rust
526    /// use razeri_plugin_contract::data_type::VarCharDataType;
527    ///
528    /// let varchar = VarCharDataType::new(100);
529    /// assert_eq!(varchar.max_length, Some(100));
530    /// ```
531    #[must_use]
532    pub fn new(max_length: u32) -> Self {
533        Self { max_length: Some(max_length), encoding: StringEncoding::Unicode }
534    }
535
536    /// Creates an unlimited length text type (`VARCHAR(MAX)`, `TEXT`, `STRING`).
537    ///
538    /// # Example
539    ///
540    /// ```rust
541    /// use razeri_plugin_contract::data_type::VarCharDataType;
542    ///
543    /// let text = VarCharDataType::unlimited();
544    /// assert_eq!(text.max_length, None);
545    /// ```
546    #[must_use]
547    pub fn unlimited() -> Self {
548        Self { max_length: None, encoding: StringEncoding::Unicode }
549    }
550
551    /// Creates a VARCHAR with the specified length and encoding.
552    ///
553    /// # Example
554    ///
555    /// ```rust
556    /// use razeri_plugin_contract::data_type::{VarCharDataType, StringEncoding};
557    ///
558    /// let ascii = VarCharDataType::with_encoding(Some(50), StringEncoding::Ascii);
559    /// assert_eq!(ascii.max_length, Some(50));
560    /// assert_eq!(ascii.encoding, StringEncoding::Ascii);
561    /// ```
562    #[must_use]
563    pub fn with_encoding(max_length: Option<u32>, encoding: StringEncoding) -> Self {
564        Self { max_length, encoding }
565    }
566}
567
568/// Unbounded text type for large text storage.
569///
570/// Maps to: `TEXT` (`PostgreSQL`), `VARCHAR(MAX)` (SQL Server),
571/// `STRING` (Snowflake, Databricks, `ClickHouse`).
572///
573/// # Example
574///
575/// ```
576/// use razeri_plugin_contract::DataType;
577///
578/// let text = DataType::text();
579/// ```
580#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
581pub struct TextDataType;
582
583// ============================================================================
584// Binary Types
585// ============================================================================
586
587/// Fixed-length binary data.
588///
589/// Maps to: `BYTEA` (`PostgreSQL`), `BINARY(n)` (SQL Server, Snowflake),
590/// `FixedString(n)` (`ClickHouse`).
591///
592/// # Example
593///
594/// ```rust
595/// use razeri_plugin_contract::data_type::BinaryDataType;
596///
597/// let binary = BinaryDataType::new(256);
598/// assert_eq!(binary.length, 256);
599/// ```
600#[derive(Debug, Clone, PartialEq, Eq, Hash)]
601pub struct BinaryDataType {
602    /// Fixed length in bytes.
603    pub length: u32,
604}
605
606impl BinaryDataType {
607    /// Creates a fixed-length binary type with the given length in bytes.
608    ///
609    /// # Example
610    ///
611    /// ```rust
612    /// use razeri_plugin_contract::data_type::BinaryDataType;
613    ///
614    /// let binary = BinaryDataType::new(128);
615    /// assert_eq!(binary.length, 128);
616    /// ```
617    #[must_use]
618    pub fn new(length: u32) -> Self {
619        Self { length }
620    }
621}
622
623/// Variable-length binary data.
624///
625/// Maps to: `BYTEA` (`PostgreSQL`), `VARBINARY(n)` (SQL Server, Snowflake),
626/// `String` (`ClickHouse`), `BINARY` (Databricks).
627///
628/// # Example
629///
630/// ```rust
631/// use razeri_plugin_contract::data_type::VarBinaryDataType;
632///
633/// // Fixed max length
634/// let vb = VarBinaryDataType::new(1024);
635/// assert_eq!(vb.max_length, Some(1024));
636///
637/// // Unlimited length
638/// let blob = VarBinaryDataType::unlimited();
639/// assert_eq!(blob.max_length, None);
640/// ```
641#[derive(Debug, Clone, PartialEq, Eq, Hash)]
642pub struct VarBinaryDataType {
643    /// Maximum length in bytes (None = unlimited/MAX).
644    pub max_length: Option<u32>,
645}
646
647impl VarBinaryDataType {
648    /// Creates a variable-length binary type with the specified max length in bytes.
649    ///
650    /// # Example
651    ///
652    /// ```rust
653    /// use razeri_plugin_contract::data_type::VarBinaryDataType;
654    ///
655    /// let vb = VarBinaryDataType::new(2048);
656    /// assert_eq!(vb.max_length, Some(2048));
657    /// ```
658    #[must_use]
659    pub fn new(max_length: u32) -> Self {
660        Self { max_length: Some(max_length) }
661    }
662
663    /// Creates an unlimited-length binary type (BYTEA, VARBINARY(MAX), etc.).
664    ///
665    /// # Example
666    ///
667    /// ```rust
668    /// use razeri_plugin_contract::data_type::VarBinaryDataType;
669    ///
670    /// let blob = VarBinaryDataType::unlimited();
671    /// assert_eq!(blob.max_length, None);
672    /// ```
673    #[must_use]
674    pub fn unlimited() -> Self {
675        Self { max_length: None }
676    }
677}
678
679// ============================================================================
680// Date and Time Types
681// ============================================================================
682
683/// Date only (year, month, day) without time component.
684///
685/// Maps to: `DATE` (all databases), `Date`/`Date32` (`ClickHouse`).
686///
687/// # Example
688///
689/// ```
690/// use razeri_plugin_contract::DataType;
691///
692/// let date = DataType::date();
693/// ```
694#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
695pub struct DateDataType;
696
697/// Time only without date component.
698///
699/// Maps to: `TIME` (`PostgreSQL`, SQL Server, Snowflake), not directly supported in
700/// `ClickHouse`/Databricks.
701///
702/// # Precision
703///
704/// - `PostgreSQL`: 0-6
705/// - SQL Server: 0-7
706/// - Snowflake: 0-9
707/// - `ClickHouse`: 0-9
708///
709/// # Example
710///
711/// ```rust
712/// use razeri_plugin_contract::data_type::TimeDataType;
713///
714/// // Default precision: 6, no timezone
715/// let time = TimeDataType::default();
716/// assert_eq!(time.precision, 6);
717/// assert!(!time.with_timezone);
718///
719/// // Precision 3, with timezone
720/// let tz_time = TimeDataType::with_timezone(3);
721/// assert_eq!(tz_time.precision, 3);
722/// assert!(tz_time.with_timezone);
723/// ```
724#[derive(Debug, Clone, PartialEq, Eq, Hash)]
725pub struct TimeDataType {
726    /// Fractional seconds precision (0-7 for SQL Server, 0-6 for `PostgreSQL`, 0-9 for Snowflake).
727    pub precision: u8,
728
729    /// Whether to include timezone offset.
730    ///
731    /// Maps to: `TIME WITH TIME ZONE` (`PostgreSQL`), not supported in SQL Server.
732    pub with_timezone: bool,
733}
734
735impl TimeDataType {
736    /// Creates a new `TimeDataType` with the given fractional seconds precision.
737    ///
738    /// # Panics
739    ///
740    /// Panics if `precision` exceeds 9.
741    ///
742    /// # Example
743    ///
744    /// ```rust
745    /// use razeri_plugin_contract::data_type::TimeDataType;
746    ///
747    /// let time = TimeDataType::new(3);
748    /// assert_eq!(time.precision, 3);
749    /// assert!(!time.with_timezone);
750    /// ```
751    ///
752    /// ```rust,should_panic
753    /// use razeri_plugin_contract::data_type::TimeDataType;
754    ///
755    /// let _ = TimeDataType::new(10); // panics: precision must be 0-9
756    /// ```
757    #[must_use]
758    pub fn new(precision: u8) -> Self {
759        assert!(precision <= 9, "Time precision must be 0-9");
760        Self { precision, with_timezone: false }
761    }
762
763    /// Creates a `TimeDataType` with timezone and the given precision.
764    ///
765    /// # Panics
766    ///
767    /// Panics if `precision` exceeds 9.
768    ///
769    /// # Example
770    ///
771    /// ```rust
772    /// use razeri_plugin_contract::data_type::TimeDataType;
773    ///
774    /// let tz_time = TimeDataType::with_timezone(3);
775    /// assert_eq!(tz_time.precision, 3);
776    /// assert!(tz_time.with_timezone);
777    /// ```
778    #[must_use]
779    pub fn with_timezone(precision: u8) -> Self {
780        assert!(precision <= 9, "Time precision must be 0-9");
781        Self { precision, with_timezone: true }
782    }
783}
784
785impl Default for TimeDataType {
786    fn default() -> Self {
787        Self { precision: 6, with_timezone: false }
788    }
789}
790
791/// Timestamp (date and time) without timezone.
792///
793/// Maps to: `TIMESTAMP` (`PostgreSQL`), `DATETIME2` (SQL Server),
794/// `TIMESTAMP_NTZ` (Snowflake), `TIMESTAMP` (Databricks), `DateTime` (`ClickHouse`).
795///
796/// # Precision
797///
798/// - `PostgreSQL`: 0-6
799/// - SQL Server: 0-7
800/// - Snowflake: 0-9
801/// - `ClickHouse`: 0-9
802///
803/// # Example
804///
805/// ```rust
806/// use razeri_plugin_contract::data_type::{DataType, TimestampDataType};
807///
808/// // Default: precision 6
809/// let ts = TimestampDataType::default();
810/// assert_eq!(ts.precision, 6);
811///
812/// // Custom precision
813/// let ts3 = TimestampDataType::new(3);
814/// assert_eq!(ts3.precision, 3);
815///
816/// // Via DataType convenience
817/// let created = DataType::timestamp();
818/// ```
819#[derive(Debug, Clone, PartialEq, Eq, Hash)]
820pub struct TimestampDataType {
821    /// Fractional seconds precision (0-9).
822    ///
823    /// - `PostgreSQL`: 0-6
824    /// - SQL Server: 0-7
825    /// - Snowflake: 0-9
826    /// - `ClickHouse`: 0-9
827    pub precision: u8,
828}
829
830impl TimestampDataType {
831    /// Creates a new `TimestampDataType` with the given fractional seconds precision.
832    ///
833    /// # Panics
834    ///
835    /// Panics if `precision` exceeds 9.
836    ///
837    /// # Example
838    ///
839    /// ```rust
840    /// use razeri_plugin_contract::data_type::TimestampDataType;
841    ///
842    /// let ts = TimestampDataType::new(3);
843    /// assert_eq!(ts.precision, 3);
844    /// ```
845    ///
846    /// ```should_panic
847    /// use razeri_plugin_contract::data_type::TimestampDataType;
848    ///
849    /// let _ = TimestampDataType::new(10); // panics: precision must be 0-9
850    /// ```
851    #[must_use]
852    pub fn new(precision: u8) -> Self {
853        assert!(precision <= 9, "Timestamp precision must be 0-9");
854        Self { precision }
855    }
856}
857
858impl Default for TimestampDataType {
859    fn default() -> Self {
860        Self { precision: 6 }
861    }
862}
863
864/// Timestamp with timezone awareness.
865///
866/// Maps to: `TIMESTAMPTZ` (`PostgreSQL`), `DATETIMEOFFSET` (SQL Server),
867/// `TIMESTAMP_TZ`/`TIMESTAMP_LTZ` (Snowflake), `TIMESTAMP` (Databricks),
868/// `DateTime64` (`ClickHouse`).
869///
870/// # Example
871///
872/// ```rust
873/// use razeri_plugin_contract::DataType;
874/// use razeri_plugin_contract::data_type::TimestampTzDataType;
875///
876/// // Default: precision 6
877/// let ts = TimestampTzDataType::default();
878/// assert_eq!(ts.precision, 6);
879///
880/// // Via DataType convenience
881/// let created_tz = DataType::timestamp_tz();
882/// ```
883#[derive(Debug, Clone, PartialEq, Eq, Hash)]
884pub struct TimestampTzDataType {
885    /// Fractional seconds precision (0-9).
886    pub precision: u8,
887}
888
889impl TimestampTzDataType {
890    /// Creates a new `TimestampTzDataType` with the given fractional seconds precision.
891    ///
892    /// # Panics
893    ///
894    /// Panics if `precision` exceeds 9.
895    ///
896    /// # Example
897    ///
898    /// ```rust
899    /// use razeri_plugin_contract::data_type::TimestampTzDataType;
900    ///
901    /// let ts = TimestampTzDataType::new(3);
902    /// assert_eq!(ts.precision, 3);
903    /// ```
904    ///
905    /// ```should_panic
906    /// use razeri_plugin_contract::data_type::TimestampTzDataType;
907    ///
908    /// let _ = TimestampTzDataType::new(10); // panics: precision must be 0-9
909    /// ```
910    #[must_use]
911    pub fn new(precision: u8) -> Self {
912        assert!(precision <= 9, "Timestamp precision must be 0-9");
913        Self { precision }
914    }
915}
916
917impl Default for TimestampTzDataType {
918    fn default() -> Self {
919        Self { precision: 6 }
920    }
921}
922
923/// Combined date and time for legacy compatibility.
924///
925/// Maps to: `DATETIME` (SQL Server legacy), `TIMESTAMP` (`PostgreSQL`).
926/// Prefer [`TimestampDataType`] for new designs.
927///
928/// # Example
929///
930/// ```rust
931/// use razeri_plugin_contract::data_type::DateTimeDataType;
932///
933/// let dt = DateTimeDataType::new(3, true);
934/// assert_eq!(dt.precision, 3);
935/// assert!(dt.with_timezone);
936/// ```
937#[derive(Debug, Clone, PartialEq, Eq, Hash)]
938pub struct DateTimeDataType {
939    /// Fractional seconds precision (0-9).
940    pub precision: u8,
941
942    /// Whether to include timezone offset.
943    pub with_timezone: bool,
944}
945
946impl DateTimeDataType {
947    /// Creates a new `DateTimeDataType` with the given precision and timezone flag.
948    ///
949    /// # Example
950    ///
951    /// ```rust
952    /// use razeri_plugin_contract::data_type::DateTimeDataType;
953    ///
954    /// let dt = DateTimeDataType::new(6, false);
955    /// assert_eq!(dt.precision, 6);
956    /// assert!(!dt.with_timezone);
957    /// ```
958    #[must_use]
959    pub fn new(precision: u8, with_timezone: bool) -> Self {
960        Self { precision, with_timezone }
961    }
962}
963
964impl Default for DateTimeDataType {
965    fn default() -> Self {
966        Self { precision: 6, with_timezone: false }
967    }
968}
969
970/// Interval/duration type for time spans.
971///
972/// Maps to: `INTERVAL` (`PostgreSQL`, Snowflake, Databricks), not directly supported in
973/// SQL Server/`ClickHouse`.
974///
975/// # Example
976///
977/// ```rust
978/// use razeri_plugin_contract::data_type::IntervalDataType;
979///
980/// let ym = IntervalDataType::YearMonth;
981/// let dt = IntervalDataType::DayTime;
982///
983/// assert_eq!(format!("{:?}", ym), "YearMonth");
984/// assert_eq!(format!("{:?}", dt), "DayTime");
985/// ```
986#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
987pub enum IntervalDataType {
988    /// Year to month intervals.
989    YearMonth,
990    /// Day to second intervals (with optional fractional seconds precision).
991    DayTime,
992}
993
994// ============================================================================
995// Special Types
996// ============================================================================
997
998/// Universally Unique Identifier (UUID/GUID).
999///
1000/// Maps to: `UUID` (`PostgreSQL`), `UNIQUEIDENTIFIER` (SQL Server),
1001/// `STRING` (Snowflake, Databricks), `UUID` (`ClickHouse`).
1002///
1003/// # Example
1004///
1005/// ```
1006/// use razeri_plugin_contract::DataType;
1007///
1008/// let uuid = DataType::uuid();
1009/// ```
1010#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1011pub struct UuidDataType;
1012
1013/// JSON storage format preference.
1014///
1015/// Determines whether JSON data is stored as text (preserving whitespace and key order)
1016/// or in a binary-optimized format (normalized, faster queries, indexable).
1017///
1018/// # Example
1019///
1020/// ```rust
1021/// use razeri_plugin_contract::data_type::JsonStorageFormat;
1022///
1023/// let text = JsonStorageFormat::Text;
1024/// let binary = JsonStorageFormat::Binary;
1025///
1026/// assert_eq!(format!("{:?}", text), "Text");
1027/// assert_eq!(format!("{:?}", binary), "Binary");
1028/// assert!(!text.is_binary());
1029/// assert!(binary.is_binary());
1030/// ```
1031#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1032pub enum JsonStorageFormat {
1033    /// Text-based JSON storage (preserves whitespace, key order).
1034    ///
1035    /// Maps to: `JSON` (`PostgreSQL`), `NVARCHAR(MAX)` (SQL Server),
1036    /// `STRING` (Databricks).
1037    Text,
1038    /// Binary-optimized JSON storage (normalized, faster queries, indexable).
1039    ///
1040    /// Maps to: `JSONB` (`PostgreSQL`), `VARIANT` (Snowflake), `JSON` (`ClickHouse`).
1041    ///
1042    /// This is the recommended default for most use cases.
1043    #[default]
1044    Binary,
1045}
1046
1047impl JsonStorageFormat {
1048    /// Returns `true` if this is the binary storage format.
1049    ///
1050    /// # Example
1051    ///
1052    /// ```rust
1053    /// use razeri_plugin_contract::data_type::JsonStorageFormat;
1054    ///
1055    /// assert!(!JsonStorageFormat::Text.is_binary());
1056    /// assert!(JsonStorageFormat::Binary.is_binary());
1057    /// ```
1058    #[must_use]
1059    pub const fn is_binary(&self) -> bool {
1060        matches!(self, Self::Binary)
1061    }
1062}
1063
1064/// JSON/Semi-structured data type.
1065///
1066/// Database mapping details:
1067///
1068/// | Platform | Type | Notes |
1069/// |----------|------|-------|
1070/// | **`PostgreSQL`** | `JSON` or `JSONB` | JSONB supports GIN indexing and is faster for queries |
1071/// | **SQL Server** | `NVARCHAR(MAX)` with JSON functions (2016+), or native JSON (2025+) | |
1072/// | **Snowflake** | `VARIANT` | Native semi-structured type, supports nested data and arrays |
1073/// | **Databricks** | `STRING` with JSON functions, or `VARIANT` | |
1074/// | **`ClickHouse`** | `JSON` (experimental/production), or `String` with JSON functions | |
1075/// | **`CitusDB`** | Same as `PostgreSQL` (JSONB recommended) | |
1076///
1077/// # Storage Formats
1078///
1079/// - **Binary** (default): Normalized storage, faster queries, indexable.
1080///   Maps to `JSONB` in `PostgreSQL`, `VARIANT` in Snowflake.
1081/// - **Text**: Preserves whitespace, key order, and duplicate keys.
1082///   Maps to `JSON` in `PostgreSQL`, `NVARCHAR(MAX)` in SQL Server.
1083///
1084/// # Example
1085///
1086/// ```rust
1087/// use razeri_plugin_contract::DataType;
1088/// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1089///
1090/// // Binary format (recommended) - uses JSONB in PostgreSQL, VARIANT in Snowflake
1091/// let json_binary = JsonDataType::default();
1092/// assert_eq!(json_binary.storage_format, JsonStorageFormat::Binary);
1093///
1094/// // Text format - preserves original formatting
1095/// let json_text = JsonDataType::text();
1096/// assert_eq!(json_text.storage_format, JsonStorageFormat::Text);
1097///
1098/// // Via DataType convenience
1099/// let data = DataType::json();
1100/// ```
1101#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
1102pub struct JsonDataType {
1103    /// Storage format preference (binary is recommended for most use cases).
1104    pub storage_format: JsonStorageFormat,
1105
1106    /// Whether to validate JSON on insert (if supported by the database).
1107    ///
1108    /// `PostgreSQL` always validates, some other databases may not.
1109    pub validate_on_insert: bool,
1110}
1111
1112impl JsonDataType {
1113    /// Creates a new JSON type with binary storage format (JSONB in `PostgreSQL`).
1114    ///
1115    /// This is the recommended format for most use cases as it enables:
1116    /// - Faster query performance
1117    /// - GIN indexing support (`PostgreSQL`)
1118    /// - Normalized storage (no duplicate keys, sorted keys)
1119    ///
1120    /// # Example
1121    ///
1122    /// ```rust
1123    /// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1124    ///
1125    /// let json = JsonDataType::new();
1126    /// assert_eq!(json.storage_format, JsonStorageFormat::Binary);
1127    /// assert!(!json.validate_on_insert);
1128    /// ```
1129    #[must_use]
1130    pub fn new() -> Self {
1131        Self::default()
1132    }
1133
1134    /// Creates a JSON type with text storage format.
1135    ///
1136    /// Use this when you need to preserve:
1137    /// - Original whitespace formatting
1138    /// - Original key order
1139    /// - Duplicate keys (not recommended but sometimes required)
1140    ///
1141    /// # Example
1142    ///
1143    /// ```rust
1144    /// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1145    ///
1146    /// let json = JsonDataType::text();
1147    /// assert_eq!(json.storage_format, JsonStorageFormat::Text);
1148    /// assert!(json.validate_on_insert);
1149    /// ```
1150    #[must_use]
1151    pub fn text() -> Self {
1152        Self { storage_format: JsonStorageFormat::Text, validate_on_insert: true }
1153    }
1154
1155    /// Creates a JSON type with binary storage format.
1156    ///
1157    /// # Example
1158    ///
1159    /// ```rust
1160    /// use razeri_plugin_contract::data_type::{JsonDataType, JsonStorageFormat};
1161    ///
1162    /// let json = JsonDataType::binary();
1163    /// assert_eq!(json.storage_format, JsonStorageFormat::Binary);
1164    /// assert!(json.validate_on_insert);
1165    /// ```
1166    #[must_use]
1167    pub fn binary() -> Self {
1168        Self { storage_format: JsonStorageFormat::Binary, validate_on_insert: true }
1169    }
1170
1171    /// Sets whether to validate JSON on insert.
1172    ///
1173    /// # Example
1174    ///
1175    /// ```rust
1176    /// use razeri_plugin_contract::data_type::JsonDataType;
1177    ///
1178    /// let json = JsonDataType::new().with_validation(false);
1179    /// assert!(!json.validate_on_insert);
1180    /// ```
1181    #[must_use]
1182    pub fn with_validation(mut self, validate: bool) -> Self {
1183        self.validate_on_insert = validate;
1184        self
1185    }
1186}
1187
1188// ============================================================================
1189// Complex/Collection Types
1190// ============================================================================
1191
1192/// Array type containing elements of another type.
1193///
1194/// Supported by: `PostgreSQL`, `ClickHouse`, Snowflake (ARRAY), Databricks (ARRAY).
1195/// Not directly supported by SQL Server.
1196///
1197/// # Example
1198///
1199/// ```rust
1200/// use razeri_plugin_contract::{DataType, data_type::ArrayDataType};
1201///
1202/// let int_array = ArrayDataType::new(DataType::int());
1203/// assert_eq!(*int_array.element_type, DataType::int());
1204///
1205/// let _str_array = ArrayDataType::new(DataType::varchar(100));
1206/// ```
1207#[derive(Debug, Clone, PartialEq, Eq)]
1208pub struct ArrayDataType {
1209    /// The data type of array elements.
1210    pub element_type: Box<DataType>,
1211}
1212
1213impl ArrayDataType {
1214    /// Creates a new array type with the given element type.
1215    ///
1216    /// # Example
1217    ///
1218    /// ```rust
1219    /// use razeri_plugin_contract::{DataType, data_type::ArrayDataType};
1220    ///
1221    /// let arr = ArrayDataType::new(DataType::bigint());
1222    /// assert_eq!(*arr.element_type, DataType::bigint());
1223    /// ```
1224    #[must_use]
1225    pub fn new(element_type: DataType) -> Self {
1226        Self { element_type: Box::new(element_type) }
1227    }
1228}
1229
1230/// Map/Dictionary type with key-value pairs.
1231///
1232/// Supported by: `ClickHouse`, Snowflake (OBJECT), Databricks (MAP).
1233/// `PostgreSQL` uses JSONB, SQL Server uses JSON string.
1234///
1235/// # Example
1236///
1237/// ```rust
1238/// use razeri_plugin_contract::DataType;
1239/// use razeri_plugin_contract::data_type::MapDataType;
1240///
1241/// let map = MapDataType::new(DataType::varchar(50), DataType::int());
1242/// assert_eq!(*map.key_type, DataType::varchar(50));
1243/// assert_eq!(*map.value_type, DataType::int());
1244/// ```
1245#[derive(Debug, Clone, PartialEq, Eq)]
1246pub struct MapDataType {
1247    /// The data type of map keys.
1248    pub key_type: Box<DataType>,
1249
1250    /// The data type of map values.
1251    pub value_type: Box<DataType>,
1252}
1253
1254impl MapDataType {
1255    /// Creates a new map type with the given key and value types.
1256    ///
1257    /// # Example
1258    ///
1259    /// ```rust
1260    /// use razeri_plugin_contract::DataType;
1261    /// use razeri_plugin_contract::data_type::MapDataType;
1262    ///
1263    /// let map = MapDataType::new(DataType::varchar(50), DataType::int());
1264    /// assert_eq!(*map.key_type, DataType::varchar(50));
1265    /// assert_eq!(*map.value_type, DataType::int());
1266    /// ```
1267    #[must_use]
1268    pub fn new(key_type: DataType, value_type: DataType) -> Self {
1269        Self { key_type: Box::new(key_type), value_type: Box::new(value_type) }
1270    }
1271}
1272
1273// ============================================================================
1274// Unified Data Type Enum
1275// ============================================================================
1276
1277/// Unified data type enumeration covering all supported types across databases.
1278///
1279/// This is the **primary type system** for Razeri. Every field, column, and
1280/// parameter in a design document is typed using `DataType`. The enum captures
1281/// enough metadata to generate correct DDL for all 8 dialect plugins while
1282/// remaining abstract over platform-specific implementation details.
1283///
1284/// # Type Categories
1285///
1286/// | Category | Variants | Example |
1287/// |----------|----------|---------|
1288/// | Numeric | `Integer`, `Decimal`, `Float` | `INT`, `DECIMAL(19,4)`, `DOUBLE` |
1289/// | Boolean | `Boolean` | `BOOLEAN`, `BIT` |
1290/// | String | `Char`, `VarChar`, `Text` | `VARCHAR(100)`, `TEXT` |
1291/// | Binary | `Binary`, `VarBinary` | `BYTEA`, `VARBINARY` |
1292/// | Date/Time | `Date`, `Time`, `Timestamp`, `TimestampTz`, `DateTime`, `Interval` | `DATE`, `TIMESTAMP` |
1293/// | Special | `Uuid`, `Json` | `UUID`, `JSONB` |
1294/// | Collection | `Array`, `Map` | `INTEGER[]`, `MAP<TEXT,INT>` |
1295///
1296/// # Design Principles
1297///
1298/// 1. **Abstract over database-specific implementations** — `DataType` captures
1299///    enough metadata to generate correct DDL for each target, but does not
1300///    embed platform-specific syntax.
1301/// 2. **Preserve precision metadata** — Decimal types carry precision and scale;
1302///    string types carry length constraints; timestamp types carry fractional
1303///    seconds precision.
1304/// 3. **Support intersection of capabilities** — All types in the enum can be
1305///    rendered by all 8 dialect plugins, even if some platforms emulate a type
1306///    (e.g., JSON stored as `NVARCHAR(MAX)` in SQL Server).
1307///
1308/// # Usage
1309///
1310/// ```
1311/// use razeri_plugin_contract::DataType;
1312///
1313/// // Convenience constructors for common types
1314/// let id = DataType::bigint();
1315/// let name = DataType::varchar(255);
1316/// let price = DataType::decimal(19, 4);
1317/// let created = DataType::timestamp();
1318///
1319/// // JSON with binary storage (JSONB in PostgreSQL, VARIANT in Snowflake)
1320/// let data = DataType::json();
1321/// ```
1322///
1323/// # Cross-Database Mapping
1324///
1325/// Each variant maps to platform-specific types as documented in the variant
1326/// definitions. Dialect plugins resolve the final SQL type string during
1327/// rendering. See `razeri-render-api` for
1328/// the rendering trait definitions.
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}