Skip to main content

rorm_declaration/
imr.rs

1//! The Internal Model Representation used by our migration cli tool
2use std::fmt::Display;
3use std::fmt::Formatter;
4use std::hash::Hash;
5use std::hash::Hasher;
6
7use ordered_float::OrderedFloat;
8use serde::Deserialize;
9use serde::Serialize;
10
11/// A collection of all models used in the resulting application
12#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
13#[serde(rename_all = "PascalCase")]
14pub struct InternalModelFormat {
15    /// List of all models
16    pub models: Vec<Model>,
17}
18
19/// A single model i.e. database table
20#[derive(Serialize, Deserialize, Debug, Clone)]
21#[serde(rename_all = "PascalCase")]
22pub struct Model {
23    /// Name of the table
24    pub name: String,
25
26    /// List of columns of the table
27    pub fields: Vec<Field>,
28
29    /// Optional source reference to enhance error messages
30    #[serde(default)]
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub source_defined_at: Option<Source>,
33}
34
35impl PartialEq for Model {
36    fn eq(&self, other: &Self) -> bool {
37        self.name == other.name && self.fields == other.fields
38    }
39}
40
41impl Hash for Model {
42    fn hash<H: Hasher>(&self, state: &mut H) {
43        self.fields.hash(state);
44        self.name.hash(state);
45    }
46
47    fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
48    where
49        Self: Sized,
50    {
51        data.iter().for_each(|x| x.hash(state));
52    }
53}
54
55/// Model's fields i.e. the table's columns
56#[derive(Serialize, Deserialize, Debug, Clone)]
57#[serde(rename_all = "PascalCase")]
58pub struct Field {
59    /// Name of the column
60    pub name: String,
61
62    /// Type of the column
63    #[serde(rename = "Type")]
64    pub db_type: DbType,
65
66    /// List of annotations, constraints, etc.
67    pub annotations: Vec<Annotation>,
68
69    /// Optional source reference to enhance error messages
70    #[serde(default)]
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub source_defined_at: Option<Source>,
73}
74
75impl PartialEq for Field {
76    fn eq(&self, other: &Self) -> bool {
77        self.name == other.name
78            && self.db_type == other.db_type
79            && self.annotations == other.annotations
80    }
81}
82
83impl Hash for Field {
84    fn hash<H: Hasher>(&self, state: &mut H) {
85        self.name.hash(state);
86        self.annotations.hash(state);
87        self.db_type.hash(state);
88    }
89
90    fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
91    where
92        Self: Sized,
93    {
94        data.iter().for_each(|x| x.hash(state));
95    }
96}
97
98/// Location in the source code a [Model] or [Field] originates from
99/// Used for better error messages in the migration tool
100#[derive(Serialize, Deserialize, Debug, Clone, Hash)]
101#[serde(rename_all = "PascalCase")]
102pub struct Source {
103    /// Filename of the source code of the [Model] or [Field]
104    pub file: String,
105    /// Line of the [Model] or [Field]
106    pub line: usize,
107    /// Column of the [Model] or [Field]
108    pub column: usize,
109}
110
111/// All column types supported by the migration tool
112#[allow(missing_docs)]
113#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, PartialEq, Eq)]
114#[serde(rename_all = "lowercase")]
115pub enum DbType {
116    VarChar,
117    Binary,
118    Int8,
119    Int16,
120    Int32,
121    Int64,
122    #[serde(rename = "float_number")]
123    Float,
124    #[serde(rename = "double_number")]
125    Double,
126    Boolean,
127    Date,
128    DateTime,
129    Timestamp,
130    Time,
131    Choices,
132    Uuid,
133    MacAddress,
134    IpNetwork,
135    BitVec,
136}
137
138/// The subset of annotations which need to be communicated with the migration tool
139#[non_exhaustive]
140#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
141#[serde(tag = "Type", content = "Value")]
142#[serde(rename_all = "snake_case")]
143pub enum Annotation {
144    /// Only for [DbType::Timestamp], [DbType::DateTime], [DbType::Time] and [DbType::Date].
145    /// Will set the current time of the database when a row is created.
146    AutoCreateTime,
147    /// Only for [DbType::Timestamp], [DbType::DateTime], [DbType::Time] and [DbType::Date].
148    /// Will set the current time of the database when a row is updated.
149    AutoUpdateTime,
150    /// AUTO_INCREMENT constraint
151    AutoIncrement,
152    /// A list of choices to set
153    Choices(Vec<String>),
154    /// DEFAULT constraint
155    DefaultValue(DefaultValue),
156    /// Create an index. The optional [IndexValue] can be used, to build more complex indexes.
157    Index(Option<IndexValue>),
158    /// Only for VARCHAR, VARBINARY. Specifies the maximum length of the column's content.
159    MaxLength(i32),
160    /// NOT NULL constraint
161    NotNull,
162    /// The annotated column will be used as primary key
163    PrimaryKey,
164    /// UNIQUE constraint
165    Unique,
166    /// Foreign Key constraint
167    ForeignKey(ForeignKey),
168}
169
170/// Represents a foreign key
171#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default)]
172#[serde(rename_all = "PascalCase")]
173pub struct ForeignKey {
174    /// Name of the table that should be referenced
175    pub table_name: String,
176    /// Name of the column that should be referenced
177    pub column_name: String,
178    /// Action to be used in case of on delete
179    pub on_delete: ReferentialAction,
180    /// Action to be used in case of an update
181    pub on_update: ReferentialAction,
182}
183
184/**
185Action that gets trigger on update and on delete.
186*/
187#[derive(Serialize, Deserialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
188#[serde(rename_all = "PascalCase")]
189pub enum ReferentialAction {
190    /// Stop operation if any keys still depend on the parent table
191    Restrict,
192    /// The action is cascaded
193    Cascade,
194    /// The field is set to null
195    SetNull,
196    /// The field is set to its default
197    SetDefault,
198}
199
200impl Default for ReferentialAction {
201    fn default() -> Self {
202        Self::Restrict
203    }
204}
205
206impl Display for ReferentialAction {
207    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208        match self {
209            ReferentialAction::Restrict => write!(f, "RESTRICT"),
210            ReferentialAction::Cascade => write!(f, "CASCADE"),
211            ReferentialAction::SetNull => write!(f, "SET NULL"),
212            ReferentialAction::SetDefault => write!(f, "SET DEFAULT"),
213        }
214    }
215}
216
217/// Represents a complex index
218#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
219#[serde(rename_all = "PascalCase")]
220pub struct IndexValue {
221    /// Name of the index. Can be used multiple times in a [Model] to create an
222    /// index with multiple columns.
223    pub name: String,
224
225    /// The order to put the columns in while generating an index.
226    /// Only useful if multiple columns with the same name are present.
227    #[serde(default)]
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub priority: Option<i32>,
230}
231
232/// A column's default value which is any non object / array json value
233#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
234#[serde(untagged)]
235pub enum DefaultValue {
236    /// Use hexadecimal to represent binary data
237    String(String),
238    /// i64 is used as it can represent any integer defined in DbType
239    Integer(i64),
240    /// Ordered float is used as f64 does not Eq and Order which are needed for Hash
241    Float(OrderedFloat<f64>),
242    /// Just a bool. Nothing interesting here.
243    Boolean(bool),
244}