drizzle_types/sqlite/ddl/
index.rs1use crate::alloc_prelude::*;
5
6#[cfg(feature = "serde")]
7use crate::serde_helpers::{cow_from_string, cow_option_from_string};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
17pub enum IndexOrigin {
18 #[default]
20 Manual,
21 Auto,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
33pub struct IndexColumnDef {
34 pub value: &'static str,
36 #[cfg_attr(feature = "serde", serde(default))]
38 pub is_expression: bool,
39}
40
41impl IndexColumnDef {
42 #[must_use]
44 pub const fn new(value: &'static str) -> Self {
45 Self {
46 value,
47 is_expression: false,
48 }
49 }
50
51 #[must_use]
53 pub const fn expression(value: &'static str) -> Self {
54 Self {
55 value,
56 is_expression: true,
57 }
58 }
59
60 #[must_use]
62 pub const fn into_column(self) -> IndexColumn {
63 IndexColumn {
64 value: Cow::Borrowed(self.value),
65 is_expression: self.is_expression,
66 }
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
74pub struct IndexColumn {
75 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
77 pub value: Cow<'static, str>,
78 #[cfg_attr(feature = "serde", serde(default))]
80 pub is_expression: bool,
81}
82
83impl IndexColumn {
84 #[must_use]
86 pub fn new(value: impl Into<Cow<'static, str>>) -> Self {
87 Self {
88 value: value.into(),
89 is_expression: false,
90 }
91 }
92
93 #[must_use]
95 pub fn expression(value: impl Into<Cow<'static, str>>) -> Self {
96 Self {
97 value: value.into(),
98 is_expression: true,
99 }
100 }
101}
102
103impl IndexColumn {
104 #[must_use]
107 pub fn to_sql(&self) -> String {
108 if self.is_expression {
109 format!("({})", self.value)
110 } else {
111 format!("`{}`", self.value.replace('`', "``"))
112 }
113 }
114}
115
116impl From<IndexColumnDef> for IndexColumn {
117 fn from(def: IndexColumnDef) -> Self {
118 def.into_column()
119 }
120}
121
122#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
139pub struct IndexDef {
140 pub table: &'static str,
142 pub name: &'static str,
144 pub columns: &'static [IndexColumnDef],
146 pub is_unique: bool,
148 pub where_clause: Option<&'static str>,
150 pub origin: IndexOrigin,
152}
153
154impl IndexDef {
155 #[must_use]
158 pub const fn new(table: &'static str, name: &'static str) -> Self {
159 Self {
160 table,
161 name,
162 columns: &[],
163 is_unique: false,
164 where_clause: None,
165 origin: IndexOrigin::Manual,
166 }
167 }
168
169 #[must_use]
171 pub const fn unique(self) -> Self {
172 Self {
173 is_unique: true,
174 ..self
175 }
176 }
177
178 #[must_use]
180 pub const fn columns(self, columns: &'static [IndexColumnDef]) -> Self {
181 Self { columns, ..self }
182 }
183
184 #[must_use]
186 pub const fn where_clause(self, clause: &'static str) -> Self {
187 Self {
188 where_clause: Some(clause),
189 ..self
190 }
191 }
192
193 #[must_use]
195 pub const fn auto_origin(self) -> Self {
196 Self {
197 origin: IndexOrigin::Auto,
198 ..self
199 }
200 }
201
202 #[must_use]
204 pub fn into_index(self) -> Index {
205 Index {
206 table: Cow::Borrowed(self.table),
207 name: Cow::Borrowed(self.name),
208 columns: self.columns.iter().map(|c| IndexColumn::from(*c)).collect(),
209 is_unique: self.is_unique,
210 where_clause: self.where_clause.map(Cow::Borrowed),
211 origin: self.origin,
212 }
213 }
214}
215
216impl Default for IndexDef {
217 fn default() -> Self {
218 Self::new("", "")
219 }
220}
221
222#[derive(Clone, Debug, PartialEq, Eq)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
230pub struct Index {
231 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
233 pub table: Cow<'static, str>,
234
235 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
237 pub name: Cow<'static, str>,
238
239 pub columns: Vec<IndexColumn>,
241
242 #[cfg_attr(feature = "serde", serde(default))]
244 pub is_unique: bool,
245
246 #[cfg_attr(
248 feature = "serde",
249 serde(
250 default,
251 skip_serializing_if = "Option::is_none",
252 rename = "where",
253 deserialize_with = "cow_option_from_string"
254 )
255 )]
256 pub where_clause: Option<Cow<'static, str>>,
257
258 #[cfg_attr(feature = "serde", serde(default))]
260 pub origin: IndexOrigin,
261}
262
263impl Index {
264 #[must_use]
266 pub fn new(
267 table: impl Into<Cow<'static, str>>,
268 name: impl Into<Cow<'static, str>>,
269 columns: Vec<IndexColumn>,
270 ) -> Self {
271 Self {
272 table: table.into(),
273 name: name.into(),
274 columns,
275 is_unique: false,
276 where_clause: None,
277 origin: IndexOrigin::Manual,
278 }
279 }
280
281 #[must_use]
283 pub const fn unique(mut self) -> Self {
284 self.is_unique = true;
285 self
286 }
287
288 #[inline]
290 #[must_use]
291 pub fn name(&self) -> &str {
292 &self.name
293 }
294
295 #[inline]
297 #[must_use]
298 pub fn table(&self) -> &str {
299 &self.table
300 }
301}
302
303impl Default for Index {
304 fn default() -> Self {
305 Self::new("", "", vec![])
306 }
307}
308
309impl From<IndexDef> for Index {
310 fn from(def: IndexDef) -> Self {
311 def.into_index()
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn column_names_with_backticks_are_escaped() {
321 let column = IndexColumn::new("a`b");
322 assert_eq!(column.to_sql(), "`a``b`");
323 }
324
325 #[test]
326 fn test_const_index_def() {
327 const COLS: &[IndexColumnDef] = &[
328 IndexColumnDef::new("email"),
329 IndexColumnDef::new("created_at"),
330 ];
331
332 const IDX: IndexDef = IndexDef::new("users", "idx_users_email")
333 .unique()
334 .columns(COLS);
335
336 assert_eq!(IDX.name, "idx_users_email");
337 assert_eq!(IDX.table, "users");
338 const {
339 assert!(IDX.is_unique);
340 }
341 assert_eq!(IDX.columns.len(), 2);
342 }
343
344 #[test]
345 fn test_expression_column() {
346 const COLS: &[IndexColumnDef] = &[IndexColumnDef::expression("lower(email)")];
347
348 const IDX: IndexDef = IndexDef::new("users", "idx_email_lower").columns(COLS);
349
350 assert!(IDX.columns[0].is_expression);
351 }
352
353 #[test]
354 fn test_index_def_to_index() {
355 const COLS: &[IndexColumnDef] = &[IndexColumnDef::new("email")];
356 const DEF: IndexDef = IndexDef::new("users", "idx_email").unique().columns(COLS);
357
358 let idx = DEF.into_index();
359 assert_eq!(idx.name(), "idx_email");
360 assert!(idx.is_unique);
361 assert_eq!(idx.columns.len(), 1);
362 }
363
364 #[test]
365 fn test_into_index() {
366 const COLS: &[IndexColumnDef] = &[IndexColumnDef::new("email")];
367 const DEF: IndexDef = IndexDef::new("users", "idx_email").unique().columns(COLS);
368 let idx = DEF.into_index();
369
370 assert_eq!(idx.name, Cow::Borrowed("idx_email"));
371 assert!(idx.is_unique);
372 }
373}