1use std::fmt;
7
8use serde::{Deserialize, Serialize};
9
10use super::column_type::ColumnType;
11use super::float_width::FloatWidth;
12use super::int_width::IntWidth;
13
14#[derive(
19 Debug,
20 Clone,
21 PartialEq,
22 Eq,
23 Hash,
24 Serialize,
25 Deserialize,
26 zerompk::ToMessagePack,
27 zerompk::FromMessagePack,
28)]
29#[msgpack(c_enum)]
30#[repr(u8)]
31pub enum ColumnModifier {
32 TimeKey = 0,
35 SpatialIndex = 1,
38}
39
40#[non_exhaustive]
45#[derive(
46 Debug,
47 Clone,
48 PartialEq,
49 Eq,
50 Serialize,
51 Deserialize,
52 zerompk::ToMessagePack,
53 zerompk::FromMessagePack,
54)]
55pub struct ColumnDef {
56 pub name: String,
57 pub column_type: ColumnType,
58 pub nullable: bool,
59 pub default: Option<String>,
60 pub primary_key: bool,
61 #[serde(default, skip_serializing_if = "Vec::is_empty")]
63 pub modifiers: Vec<ColumnModifier>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub generated_expr: Option<String>,
68 #[serde(default, skip_serializing_if = "Vec::is_empty")]
70 pub generated_deps: Vec<String>,
71 #[serde(default = "default_added_at_version")]
76 pub added_at_version: u32,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub int_width: Option<IntWidth>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub float_width: Option<FloatWidth>,
87}
88
89fn default_added_at_version() -> u32 {
90 1
91}
92
93impl ColumnDef {
94 pub fn required(name: impl Into<String>, column_type: ColumnType) -> Self {
95 Self {
96 name: name.into(),
97 column_type,
98 nullable: false,
99 default: None,
100 primary_key: false,
101 modifiers: Vec::new(),
102 generated_expr: None,
103 generated_deps: Vec::new(),
104 added_at_version: 1,
105 int_width: None,
106 float_width: None,
107 }
108 }
109
110 pub fn nullable(name: impl Into<String>, column_type: ColumnType) -> Self {
111 Self {
112 name: name.into(),
113 column_type,
114 nullable: true,
115 default: None,
116 primary_key: false,
117 modifiers: Vec::new(),
118 generated_expr: None,
119 generated_deps: Vec::new(),
120 added_at_version: 1,
121 int_width: None,
122 float_width: None,
123 }
124 }
125
126 pub fn with_declared_width(mut self, declared: &str) -> Self {
132 self.int_width = match self.column_type {
133 ColumnType::Int64 => IntWidth::from_declared_type(declared),
134 _ => None,
135 };
136 self.float_width = match self.column_type {
137 ColumnType::Float64 => FloatWidth::from_declared_type(declared),
138 _ => None,
139 };
140 self
141 }
142
143 pub fn declared_type_name(&self) -> String {
147 match (self.int_width, self.float_width) {
148 (Some(width), _) => width.pg_type_name().to_ascii_uppercase(),
149 (None, Some(width)) => width.pg_type_name().to_ascii_uppercase(),
150 (None, None) => self.column_type.to_string(),
151 }
152 }
153
154 pub fn with_primary_key(mut self) -> Self {
155 self.primary_key = true;
156 self.nullable = false;
157 self
158 }
159
160 pub fn is_time_key(&self) -> bool {
162 self.modifiers.contains(&ColumnModifier::TimeKey)
163 }
164
165 pub fn is_spatial_index(&self) -> bool {
167 self.modifiers.contains(&ColumnModifier::SpatialIndex)
168 }
169
170 pub fn with_default(mut self, expr: impl Into<String>) -> Self {
171 self.default = Some(expr.into());
172 self
173 }
174}
175
176impl fmt::Display for ColumnDef {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 write!(f, "{} {}", self.name, self.declared_type_name())?;
179 if !self.nullable {
180 write!(f, " NOT NULL")?;
181 }
182 if self.primary_key {
183 write!(f, " PRIMARY KEY")?;
184 }
185 if let Some(ref d) = self.default {
186 write!(f, " DEFAULT {d}")?;
187 }
188 Ok(())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn declared_width_lands_only_on_its_numeric_family() {
198 let small = ColumnDef::nullable("n", ColumnType::Int64).with_declared_width("SMALLINT");
199 assert_eq!(small.int_width, Some(IntWidth::I16));
200 assert_eq!(small.float_width, None);
201
202 let real = ColumnDef::nullable("r", ColumnType::Float64).with_declared_width("REAL");
203 assert_eq!(real.float_width, Some(FloatWidth::F32));
204 assert_eq!(real.int_width, None);
205
206 let text = ColumnDef::nullable("t", ColumnType::String).with_declared_width("SMALLINT");
207 assert_eq!((text.int_width, text.float_width), (None, None));
208 }
209
210 #[test]
212 fn declared_type_name_parses_back_to_the_same_column() {
213 for declared in ["SMALLINT", "INTEGER", "BIGINT", "REAL", "DOUBLE PRECISION"] {
214 let column_type: ColumnType = declared.parse().expect("declared type parses");
215 let column = ColumnDef::nullable("c", column_type).with_declared_width(declared);
216 assert_eq!(column.declared_type_name(), declared);
217 let reparsed = ColumnDef::nullable("c", column_type)
218 .with_declared_width(&column.declared_type_name());
219 assert_eq!(reparsed, column);
220 }
221 assert_eq!(
222 ColumnDef::nullable("c", ColumnType::Int64).declared_type_name(),
223 "BIGINT"
224 );
225 }
226}