Skip to content

Commit d2bb30e

Browse files
authored
Merge pull request #37 from dev-five-git/support-default
Support boolean type at default
2 parents 3949623 + ea9471c commit d2bb30e

File tree

11 files changed

+194
-27
lines changed

11 files changed

+194
-27
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"crates/vespertide-cli/Cargo.toml":"Patch","crates/vespertide-naming/Cargo.toml":"Patch","crates/vespertide-planner/Cargo.toml":"Patch","crates/vespertide-core/Cargo.toml":"Patch","crates/vespertide-exporter/Cargo.toml":"Patch","crates/vespertide-macro/Cargo.toml":"Patch","crates/vespertide-config/Cargo.toml":"Patch","crates/vespertide/Cargo.toml":"Patch","crates/vespertide-query/Cargo.toml":"Patch","crates/vespertide-loader/Cargo.toml":"Patch"},"note":"Support boolean type at default","date":"2025-12-24T05:46:19.710280200Z"}

Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vespertide-core/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ pub use action::{MigrationAction, MigrationPlan};
66
pub use migration::{MigrationError, MigrationOptions};
77
pub use schema::{
88
ColumnDef, ColumnName, ColumnType, ComplexColumnType, EnumValues, IndexDef, IndexName,
9-
NumValue, ReferenceAction, SimpleColumnType, StrOrBoolOrArray, TableConstraint, TableDef,
10-
TableName, TableValidationError,
9+
NumValue, ReferenceAction, SimpleColumnType, StrOrBoolOrArray, StringOrBool, TableConstraint,
10+
TableDef, TableName, TableValidationError,
1111
};

crates/vespertide-core/src/schema/column.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ use schemars::JsonSchema;
22
use serde::{Deserialize, Serialize};
33

44
use crate::schema::{
5-
foreign_key::ForeignKeySyntax, names::ColumnName, primary_key::PrimaryKeySyntax,
6-
str_or_bool::StrOrBoolOrArray,
5+
foreign_key::ForeignKeySyntax,
6+
names::ColumnName,
7+
primary_key::PrimaryKeySyntax,
8+
str_or_bool::{StrOrBoolOrArray, StringOrBool},
79
};
810

911
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
@@ -13,7 +15,7 @@ pub struct ColumnDef {
1315
pub r#type: ColumnType,
1416
pub nullable: bool,
1517
#[serde(skip_serializing_if = "Option::is_none")]
16-
pub default: Option<String>,
18+
pub default: Option<StringOrBool>,
1719
#[serde(skip_serializing_if = "Option::is_none")]
1820
pub comment: Option<String>,
1921
#[serde(skip_serializing_if = "Option::is_none")]

crates/vespertide-core/src/schema/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ pub use index::IndexDef;
1616
pub use names::{ColumnName, IndexName, TableName};
1717
pub use primary_key::PrimaryKeyDef;
1818
pub use reference::ReferenceAction;
19-
pub use str_or_bool::StrOrBoolOrArray;
19+
pub use str_or_bool::{StrOrBoolOrArray, StringOrBool};
2020
pub use table::{TableDef, TableValidationError};

crates/vespertide-core/src/schema/str_or_bool.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,81 @@ pub enum StrOrBoolOrArray {
88
Array(Vec<String>),
99
Bool(bool),
1010
}
11+
12+
/// A value that can be either a string or a boolean.
13+
/// This is used for default values where boolean columns can use `true`/`false` directly.
14+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
15+
#[serde(untagged)]
16+
pub enum StringOrBool {
17+
Bool(bool),
18+
String(String),
19+
}
20+
21+
impl StringOrBool {
22+
/// Convert to SQL string representation
23+
pub fn to_sql(&self) -> String {
24+
match self {
25+
StringOrBool::Bool(b) => b.to_string(),
26+
StringOrBool::String(s) => s.clone(),
27+
}
28+
}
29+
}
30+
31+
impl From<bool> for StringOrBool {
32+
fn from(b: bool) -> Self {
33+
StringOrBool::Bool(b)
34+
}
35+
}
36+
37+
impl From<String> for StringOrBool {
38+
fn from(s: String) -> Self {
39+
StringOrBool::String(s)
40+
}
41+
}
42+
43+
impl From<&str> for StringOrBool {
44+
fn from(s: &str) -> Self {
45+
StringOrBool::String(s.to_string())
46+
}
47+
}
48+
49+
#[cfg(test)]
50+
mod tests {
51+
use super::*;
52+
53+
#[test]
54+
fn test_string_or_bool_to_sql_bool() {
55+
let val = StringOrBool::Bool(true);
56+
assert_eq!(val.to_sql(), "true");
57+
58+
let val = StringOrBool::Bool(false);
59+
assert_eq!(val.to_sql(), "false");
60+
}
61+
62+
#[test]
63+
fn test_string_or_bool_to_sql_string() {
64+
let val = StringOrBool::String("hello".into());
65+
assert_eq!(val.to_sql(), "hello");
66+
}
67+
68+
#[test]
69+
fn test_string_or_bool_from_bool() {
70+
let val: StringOrBool = true.into();
71+
assert_eq!(val, StringOrBool::Bool(true));
72+
73+
let val: StringOrBool = false.into();
74+
assert_eq!(val, StringOrBool::Bool(false));
75+
}
76+
77+
#[test]
78+
fn test_string_or_bool_from_string() {
79+
let val: StringOrBool = String::from("test").into();
80+
assert_eq!(val, StringOrBool::String("test".into()));
81+
}
82+
83+
#[test]
84+
fn test_string_or_bool_from_str() {
85+
let val: StringOrBool = "test".into();
86+
assert_eq!(val, StringOrBool::String("test".into()));
87+
}
88+
}

crates/vespertide-exporter/src/seaorm/mod.rs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::collections::HashSet;
22

33
use crate::orm::OrmExporter;
44
use vespertide_core::{
5-
ColumnDef, ColumnType, ComplexColumnType, EnumValues, NumValue, TableConstraint, TableDef,
5+
ColumnDef, ColumnType, ComplexColumnType, EnumValues, NumValue, StringOrBool, TableConstraint,
6+
TableDef,
67
};
78

89
pub struct SeaOrmExporter;
@@ -180,8 +181,15 @@ fn render_column(
180181

181182
/// Format default value for SeaORM attribute.
182183
/// Returns the full attribute string like `default_value = "..."` or `default_value = 0`.
183-
fn format_default_value(value: &str, column_type: &ColumnType) -> String {
184-
let trimmed = value.trim();
184+
fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> String {
185+
// Handle boolean values directly
186+
if let StringOrBool::Bool(b) = value {
187+
return format!("default_value = {}", b);
188+
}
189+
190+
// For string values, process as before
191+
let value_str = value.to_sql();
192+
let trimmed = value_str.trim();
185193

186194
// Remove surrounding single quotes if present (SQL string literals)
187195
let cleaned = if trimmed.starts_with('\'') && trimmed.ends_with('\'') && trimmed.len() >= 2 {
@@ -2214,4 +2222,51 @@ mod tests {
22142222
assert_snapshot!(rendered);
22152223
});
22162224
}
2225+
2226+
#[test]
2227+
fn test_boolean_default_value_with_bool_type() {
2228+
use vespertide_core::schema::primary_key::PrimaryKeySyntax;
2229+
let table = TableDef {
2230+
name: "settings".into(),
2231+
columns: vec![
2232+
ColumnDef {
2233+
name: "id".into(),
2234+
r#type: ColumnType::Simple(SimpleColumnType::Integer),
2235+
nullable: false,
2236+
default: None,
2237+
comment: None,
2238+
primary_key: Some(PrimaryKeySyntax::Bool(true)),
2239+
unique: None,
2240+
index: None,
2241+
foreign_key: None,
2242+
},
2243+
ColumnDef {
2244+
name: "is_active".into(),
2245+
r#type: ColumnType::Simple(SimpleColumnType::Boolean),
2246+
nullable: false,
2247+
default: Some(StringOrBool::Bool(true)),
2248+
comment: None,
2249+
primary_key: None,
2250+
unique: None,
2251+
index: None,
2252+
foreign_key: None,
2253+
},
2254+
ColumnDef {
2255+
name: "is_deleted".into(),
2256+
r#type: ColumnType::Simple(SimpleColumnType::Boolean),
2257+
nullable: false,
2258+
default: Some(StringOrBool::Bool(false)),
2259+
comment: None,
2260+
primary_key: None,
2261+
unique: None,
2262+
index: None,
2263+
foreign_key: None,
2264+
},
2265+
],
2266+
constraints: vec![],
2267+
};
2268+
let rendered = render_entity(&table);
2269+
assert!(rendered.contains("default_value = true"));
2270+
assert!(rendered.contains("default_value = false"));
2271+
}
22172272
}

crates/vespertide-query/src/sql/add_column.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn build_add_column(
9090
let fill_expr = if let Some(fill) = fill_with {
9191
Expr::cust(fill)
9292
} else if let Some(def) = &column.default {
93-
Expr::cust(def)
93+
Expr::cust(def.to_sql())
9494
} else {
9595
Expr::cust("NULL")
9696
};

crates/vespertide-query/src/sql/helpers.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ pub fn build_sea_column_def_with_table(
185185
}
186186

187187
if let Some(default) = &column.default {
188-
let converted = convert_default_for_backend(default, backend);
188+
let default_str = default.to_sql();
189+
let converted = convert_default_for_backend(&default_str, backend);
189190
col.default(Into::<SimpleExpr>::into(sea_query::Expr::cust(converted)));
190191
}
191192

schemas/migration.schema.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@
4343
]
4444
},
4545
"default": {
46-
"type": [
47-
"string",
48-
"null"
46+
"anyOf": [
47+
{
48+
"$ref": "#/$defs/StringOrBool"
49+
},
50+
{
51+
"type": "null"
52+
}
4953
]
5054
},
5155
"foreign_key": {
@@ -597,6 +601,17 @@
597601
}
598602
]
599603
},
604+
"StringOrBool": {
605+
"description": "A value that can be either a string or a boolean.\nThis is used for default values where boolean columns can use `true`/`false` directly.",
606+
"anyOf": [
607+
{
608+
"type": "boolean"
609+
},
610+
{
611+
"type": "string"
612+
}
613+
]
614+
},
600615
"TableConstraint": {
601616
"oneOf": [
602617
{

0 commit comments

Comments
 (0)