toasty_core/schema/db/
column.rs1use super::{TableId, Type, table};
2use crate::stmt;
3
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Column {
33 pub id: ColumnId,
35
36 pub name: String,
38
39 pub ty: stmt::Type,
41
42 pub storage_ty: Type,
44
45 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
47 pub nullable: bool,
48
49 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
51 pub primary_key: bool,
52
53 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
57 pub auto_increment: bool,
58
59 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "is_false"))]
61 pub versionable: bool,
62}
63
64impl Column {
65 pub fn is_document(&self) -> bool {
68 match &self.ty {
69 stmt::Type::Object => true,
70 stmt::Type::List(elem) => matches!(**elem, stmt::Type::Object),
71 _ => false,
72 }
73 }
74}
75
76#[cfg(feature = "serde")]
77fn is_false(b: &bool) -> bool {
78 !*b
79}
80
81#[derive(PartialEq, Eq, Clone, Copy, Hash)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub struct ColumnId {
97 pub table: TableId,
99 pub index: usize,
101}
102
103impl ColumnId {
104 pub(crate) fn placeholder() -> Self {
105 Self {
106 table: table::TableId::placeholder(),
107 index: usize::MAX,
108 }
109 }
110}
111
112impl From<&Column> for ColumnId {
113 fn from(value: &Column) -> Self {
114 value.id
115 }
116}
117
118impl fmt::Debug for ColumnId {
119 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(fmt, "ColumnId({}/{})", self.table.0, self.index)
121 }
122}
123
124#[cfg(all(test, feature = "serde"))]
125mod serde_tests {
126 use crate::schema::db::{Column, ColumnId, TableId, Type};
127 use crate::stmt;
128
129 fn base_column() -> Column {
130 Column {
131 id: ColumnId {
132 table: TableId(0),
133 index: 0,
134 },
135 name: "test".to_string(),
136 ty: stmt::Type::String,
137 storage_ty: Type::Text,
138 nullable: false,
139 primary_key: false,
140 auto_increment: false,
141 versionable: false,
142 }
143 }
144
145 #[test]
146 fn false_booleans_are_omitted() {
147 let toml = toml::to_string(&base_column()).unwrap();
148 assert!(!toml.contains("nullable"), "toml: {toml}");
149 assert!(!toml.contains("primary_key"), "toml: {toml}");
150 assert!(!toml.contains("auto_increment"), "toml: {toml}");
151 assert!(!toml.contains("versionable"), "toml: {toml}");
152 }
153
154 #[test]
155 fn nullable_true_is_included() {
156 let col = Column {
157 nullable: true,
158 ..base_column()
159 };
160 let toml = toml::to_string(&col).unwrap();
161 assert!(toml.contains("nullable = true"), "toml: {toml}");
162 }
163
164 #[test]
165 fn primary_key_true_is_included() {
166 let col = Column {
167 primary_key: true,
168 ..base_column()
169 };
170 let toml = toml::to_string(&col).unwrap();
171 assert!(toml.contains("primary_key = true"), "toml: {toml}");
172 }
173
174 #[test]
175 fn auto_increment_true_is_included() {
176 let col = Column {
177 auto_increment: true,
178 ..base_column()
179 };
180 let toml = toml::to_string(&col).unwrap();
181 assert!(toml.contains("auto_increment = true"), "toml: {toml}");
182 }
183
184 #[test]
185 fn missing_bool_fields_deserialize_as_false() {
186 let toml = "name = \"test\"\nty = \"String\"\nstorage_ty = \"Text\"\n\n[id]\ntable = 0\nindex = 0\n";
187 let col: Column = toml::from_str(toml).unwrap();
188 assert!(!col.nullable);
189 assert!(!col.primary_key);
190 assert!(!col.auto_increment);
191 assert!(!col.versionable);
192 }
193
194 #[test]
195 fn round_trip_all_true() {
196 let original = Column {
197 nullable: true,
198 primary_key: true,
199 auto_increment: true,
200 ..base_column()
201 };
202 let decoded: Column = toml::from_str(&toml::to_string(&original).unwrap()).unwrap();
203 assert_eq!(original, decoded);
204 }
205}