diff --git a/.changepacks/changepack_log_ssXX3EHcL4qB_bwu70NKR.json b/.changepacks/changepack_log_ssXX3EHcL4qB_bwu70NKR.json new file mode 100644 index 0000000..43253a4 --- /dev/null +++ b/.changepacks/changepack_log_ssXX3EHcL4qB_bwu70NKR.json @@ -0,0 +1 @@ +{"changes":{"crates/vespertide-planner/Cargo.toml":"Patch","crates/vespertide-query/Cargo.toml":"Patch","crates/vespertide-macro/Cargo.toml":"Patch","crates/vespertide-loader/Cargo.toml":"Patch","crates/vespertide-exporter/Cargo.toml":"Patch","crates/vespertide-cli/Cargo.toml":"Patch","crates/vespertide-naming/Cargo.toml":"Patch","crates/vespertide-config/Cargo.toml":"Patch","crates/vespertide/Cargo.toml":"Patch","crates/vespertide-core/Cargo.toml":"Patch"},"note":"Fix suffix issue","date":"2026-02-03T13:11:17.165271600Z"} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 70ea873..c2dc661 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2972,7 +2972,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vespertide" -version = "0.1.39" +version = "0.1.40" dependencies = [ "vespertide-core", "vespertide-macro", @@ -2980,7 +2980,7 @@ dependencies = [ [[package]] name = "vespertide-cli" -version = "0.1.39" +version = "0.1.40" dependencies = [ "anyhow", "assert_cmd", @@ -3005,7 +3005,7 @@ dependencies = [ [[package]] name = "vespertide-config" -version = "0.1.39" +version = "0.1.40" dependencies = [ "clap", "schemars", @@ -3015,7 +3015,7 @@ dependencies = [ [[package]] name = "vespertide-core" -version = "0.1.39" +version = "0.1.40" dependencies = [ "rstest", "schemars", @@ -3027,7 +3027,7 @@ dependencies = [ [[package]] name = "vespertide-exporter" -version = "0.1.39" +version = "0.1.40" dependencies = [ "insta", "rstest", @@ -3038,7 +3038,7 @@ dependencies = [ [[package]] name = "vespertide-loader" -version = "0.1.39" +version = "0.1.40" dependencies = [ "anyhow", "rstest", @@ -3053,7 +3053,7 @@ dependencies = [ [[package]] name = "vespertide-macro" -version = "0.1.39" +version = "0.1.40" dependencies = [ "proc-macro2", "quote", @@ -3070,11 +3070,11 @@ dependencies = [ [[package]] name = "vespertide-naming" -version = "0.1.39" +version = "0.1.40" [[package]] name = "vespertide-planner" -version = "0.1.39" +version = "0.1.40" dependencies = [ "insta", "rstest", @@ -3085,7 +3085,7 @@ dependencies = [ [[package]] name = "vespertide-query" -version = "0.1.39" +version = "0.1.40" dependencies = [ "insta", "rstest", diff --git a/crates/vespertide-cli/src/commands/export.rs b/crates/vespertide-cli/src/commands/export.rs index 531cbf5..f5a0d59 100644 --- a/crates/vespertide-cli/src/commands/export.rs +++ b/crates/vespertide-cli/src/commands/export.rs @@ -111,6 +111,9 @@ fn build_output_path(root: &Path, rel_path: &Path, orm: Orm) -> PathBuf { (file_name, "") }; + // Strip ".vespertide" suffix if present (e.g., "user.vespertide" -> "user") + let stem = stem.strip_suffix(".vespertide").unwrap_or(stem); + let sanitized = sanitize_filename(stem); let ext = match orm { Orm::SeaOrm => "rs", @@ -145,8 +148,19 @@ fn load_models_recursive(base: &Path) -> Result> { fn ensure_mod_chain(root: &Path, rel_path: &Path) -> Result<()> { // Only needed for SeaORM (Rust) exports to wire modules. - let mut comps: Vec = rel_path - .with_extension("") + // Strip extension and ".vespertide" suffix from filename + let path_without_ext = rel_path.with_extension(""); + let path_stripped = if let Some(stem) = path_without_ext.file_stem().and_then(|s| s.to_str()) { + let stripped_stem = stem.strip_suffix(".vespertide").unwrap_or(stem); + if let Some(parent) = path_without_ext.parent() { + parent.join(stripped_stem) + } else { + PathBuf::from(stripped_stem) + } + } else { + path_without_ext + }; + let mut comps: Vec = path_stripped .components() .filter_map(|c| { c.as_os_str() @@ -504,4 +518,54 @@ mod tests { let out2 = build_output_path(root, rel_path2, Orm::SeaOrm); assert!(out2.to_string_lossy().contains("items")); } + + #[test] + fn build_output_path_strips_vespertide_suffix() { + use std::path::Path; + let root = Path::new("src/models"); + + // .vespertide.json -> .rs (strips ".vespertide" from stem) + let rel_path = Path::new("user.vespertide.json"); + let out = build_output_path(root, rel_path, Orm::SeaOrm); + assert_eq!(out, Path::new("src/models/user.rs")); + + // Nested path with .vespertide.json + let rel_path2 = Path::new("blog/post.vespertide.json"); + let out2 = build_output_path(root, rel_path2, Orm::SeaOrm); + assert_eq!(out2, Path::new("src/models/blog/post.rs")); + + // .vespertide.yaml -> .py + let rel_path3 = Path::new("order.vespertide.yaml"); + let out3 = build_output_path(root, rel_path3, Orm::SqlAlchemy); + assert_eq!(out3, Path::new("src/models/order.py")); + + // Regular .json without .vespertide suffix still works + let rel_path4 = Path::new("item.json"); + let out4 = build_output_path(root, rel_path4, Orm::SeaOrm); + assert_eq!(out4, Path::new("src/models/item.rs")); + } + + #[test] + #[serial] + fn ensure_mod_chain_strips_vespertide_suffix() { + let tmp = tempdir().unwrap(); + let root = tmp.path().join("src/models"); + fs::create_dir_all(&root).unwrap(); + + // File with .vespertide suffix should produce mod declaration without it + ensure_mod_chain(&root, Path::new("user.vespertide.json")).unwrap(); + + let root_mod = fs::read_to_string(root.join("mod.rs")).unwrap(); + // Should be "pub mod user;" not "pub mod user_vespertide;" + assert!(root_mod.contains("pub mod user;")); + assert!(!root_mod.contains("user_vespertide")); + + // Nested path with .vespertide suffix + ensure_mod_chain(&root, Path::new("blog/post.vespertide.json")).unwrap(); + let root_mod = fs::read_to_string(root.join("mod.rs")).unwrap(); + let blog_mod = fs::read_to_string(root.join("blog/mod.rs")).unwrap(); + assert!(root_mod.contains("pub mod blog;")); + assert!(blog_mod.contains("pub mod post;")); + assert!(!blog_mod.contains("post_vespertide")); + } }