data_modelling_sdk/models/table.rs
1//! Table model for the SDK
2
3use super::column::Column;
4use super::enums::{
5 DataVaultClassification, DatabaseType, MedallionLayer, ModelingLevel, SCDPattern,
6};
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use uuid::Uuid;
11
12/// Position coordinates for table placement on canvas
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct Position {
15 /// X coordinate
16 pub x: f64,
17 /// Y coordinate
18 pub y: f64,
19}
20
21/// Table model representing a database table or data contract
22///
23/// A table represents a structured data entity with columns, metadata, and relationships.
24/// Tables can be imported from various formats (SQL, ODCS, JSON Schema, etc.) and exported
25/// to multiple formats.
26///
27/// # Example
28///
29/// ```rust
30/// use data_modelling_sdk::models::{Table, Column};
31///
32/// let table = Table::new(
33/// "users".to_string(),
34/// vec![
35/// Column::new("id".to_string(), "INT".to_string()),
36/// Column::new("name".to_string(), "VARCHAR(100)".to_string()),
37/// ],
38/// );
39/// ```
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41pub struct Table {
42 /// Unique identifier for the table (UUIDv4)
43 pub id: Uuid,
44 /// Table name (must be unique within database_type/catalog/schema scope)
45 pub name: String,
46 /// List of columns in the table
47 pub columns: Vec<Column>,
48 /// Database type (PostgreSQL, MySQL, etc.) if applicable
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub database_type: Option<DatabaseType>,
51 /// Catalog name (database name in some systems)
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub catalog_name: Option<String>,
54 /// Schema name (namespace within catalog)
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub schema_name: Option<String>,
57 /// Medallion architecture layers (Bronze, Silver, Gold)
58 #[serde(default)]
59 pub medallion_layers: Vec<MedallionLayer>,
60 /// Slowly Changing Dimension pattern (Type 1, Type 2, etc.)
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub scd_pattern: Option<SCDPattern>,
63 /// Data Vault classification (Hub, Link, Satellite)
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub data_vault_classification: Option<DataVaultClassification>,
66 /// Modeling level (Conceptual, Logical, Physical)
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub modeling_level: Option<ModelingLevel>,
69 /// Tags for categorization and filtering
70 #[serde(default)]
71 pub tags: Vec<String>,
72 /// ODCL/ODCS metadata (legacy format support)
73 #[serde(default)]
74 pub odcl_metadata: HashMap<String, serde_json::Value>,
75 /// Canvas position for visual representation
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub position: Option<Position>,
78 /// Path to YAML file if loaded from file system
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub yaml_file_path: Option<String>,
81 /// Draw.io cell ID for diagram integration
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub drawio_cell_id: Option<String>,
84 /// Quality rules and checks
85 #[serde(default)]
86 pub quality: Vec<HashMap<String, serde_json::Value>>,
87 /// Validation errors and warnings
88 #[serde(default)]
89 pub errors: Vec<HashMap<String, serde_json::Value>>,
90 /// Creation timestamp
91 pub created_at: DateTime<Utc>,
92 /// Last update timestamp
93 pub updated_at: DateTime<Utc>,
94}
95
96impl Table {
97 /// Create a new table with the given name and columns
98 ///
99 /// # Arguments
100 ///
101 /// * `name` - The table name (must be valid according to naming conventions)
102 /// * `columns` - Vector of columns for the table
103 ///
104 /// # Returns
105 ///
106 /// A new `Table` instance with a generated UUIDv4 ID and current timestamps.
107 ///
108 /// # Example
109 ///
110 /// ```rust
111 /// use data_modelling_sdk::models::{Table, Column};
112 ///
113 /// let table = Table::new(
114 /// "users".to_string(),
115 /// vec![Column::new("id".to_string(), "INT".to_string())],
116 /// );
117 /// ```
118 pub fn new(name: String, columns: Vec<Column>) -> Self {
119 let now = Utc::now();
120 // UUIDv4 everywhere (do not derive ids from natural keys like name).
121 let id = Self::generate_id(&name, None, None, None);
122 Self {
123 id,
124 name,
125 columns,
126 database_type: None,
127 catalog_name: None,
128 schema_name: None,
129 medallion_layers: Vec::new(),
130 scd_pattern: None,
131 data_vault_classification: None,
132 modeling_level: None,
133 tags: Vec::new(),
134 odcl_metadata: HashMap::new(),
135 position: None,
136 yaml_file_path: None,
137 drawio_cell_id: None,
138 quality: Vec::new(),
139 errors: Vec::new(),
140 created_at: now,
141 updated_at: now,
142 }
143 }
144
145 /// Get the unique key tuple for this table
146 ///
147 /// Returns a tuple of (database_type, name, catalog_name, schema_name) that uniquely
148 /// identifies this table within its scope. Used for detecting naming conflicts.
149 ///
150 /// # Returns
151 ///
152 /// A tuple containing the database type (as string), name, catalog name, and schema name.
153 pub fn get_unique_key(&self) -> (Option<String>, String, Option<String>, Option<String>) {
154 (
155 self.database_type.as_ref().map(|dt| format!("{:?}", dt)),
156 self.name.clone(),
157 self.catalog_name.clone(),
158 self.schema_name.clone(),
159 )
160 }
161
162 /// Generate a UUIDv4 for a new table id.
163 ///
164 /// Note: params are retained for backward-compatibility with previous deterministic-v5 API.
165 pub fn generate_id(
166 _name: &str,
167 _database_type: Option<&DatabaseType>,
168 _catalog_name: Option<&str>,
169 _schema_name: Option<&str>,
170 ) -> Uuid {
171 Uuid::new_v4()
172 }
173}