Skip to content

Commit 75a6fa6

Browse files
committed
Add new command to cli
1 parent 4c5b939 commit 75a6fa6

File tree

6 files changed

+101
-1
lines changed

6 files changed

+101
-1
lines changed

Cargo.lock

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

crates/vespertide-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ publish = false
88
anyhow = "1"
99
clap = { version = "4", features = ["derive"] }
1010
serde_json = "1"
11+
serde_yaml = "0.9"
1112
vespertide-config = { path = "../vespertide-config" }
1213
vespertide-core = { path = "../vespertide-core" }
1314
vespertide-planner = { path = "../vespertide-planner" }

crates/vespertide-cli/src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
pub mod diff;
22
pub mod init;
3+
pub mod new;
34
pub mod log;
45
pub mod sql;
56
pub mod revision;
67
pub mod status;
78

89
pub use diff::cmd_diff;
910
pub use init::cmd_init;
11+
pub use new::cmd_new;
1012
pub use log::cmd_log;
1113
pub use sql::cmd_sql;
1214
pub use revision::cmd_revision;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use std::fs;
2+
3+
use anyhow::{Context, Result, bail};
4+
use serde_json::Value;
5+
use vespertide_core::TableDef;
6+
7+
use crate::{ModelFormat, utils::load_config};
8+
9+
pub fn cmd_new(name: String, format: ModelFormat) -> Result<()> {
10+
let config = load_config()?;
11+
let dir = config.models_dir();
12+
if !dir.exists() {
13+
fs::create_dir_all(dir).context("create models directory")?;
14+
}
15+
16+
let ext = match format {
17+
ModelFormat::Json => "json",
18+
ModelFormat::Yaml => "yaml",
19+
ModelFormat::Yml => "yml",
20+
};
21+
22+
let path = dir.join(format!("{name}.{ext}"));
23+
if path.exists() {
24+
bail!("model file already exists: {}", path.display());
25+
}
26+
27+
let table = TableDef {
28+
name: name.clone(),
29+
columns: Vec::new(),
30+
constraints: Vec::new(),
31+
indexes: Vec::new(),
32+
};
33+
34+
match format {
35+
ModelFormat::Json => write_json_with_schema(&path, &table)?,
36+
ModelFormat::Yaml | ModelFormat::Yml => write_yaml(&path, &table)?,
37+
}
38+
39+
println!("Created model template: {}", path.display());
40+
Ok(())
41+
}
42+
43+
fn write_json_with_schema(path: &std::path::Path, table: &TableDef) -> Result<()> {
44+
let mut value = serde_json::to_value(table).context("serialize table to json")?;
45+
if let Value::Object(ref mut map) = value {
46+
map.insert(
47+
"$schema".to_string(),
48+
Value::String("https://json-schema.org/draft/2020-12/schema".to_string()),
49+
);
50+
}
51+
let text = serde_json::to_string_pretty(&value).context("stringify json with schema")?;
52+
fs::write(path, text).with_context(|| format!("write file: {}", path.display()))?;
53+
Ok(())
54+
}
55+
56+
fn write_yaml(path: &std::path::Path, table: &TableDef) -> Result<()> {
57+
let text = serde_yaml::to_string(table).context("serialize table to yaml")?;
58+
fs::write(path, text).with_context(|| format!("write file: {}", path.display()))?;
59+
Ok(())
60+
}

crates/vespertide-cli/src/main.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use clap::{Parser, Subcommand};
33

44
mod commands;
55
mod utils;
6-
use commands::{cmd_diff, cmd_init, cmd_log, cmd_revision, cmd_sql, cmd_status};
6+
use commands::{cmd_diff, cmd_init, cmd_log, cmd_new, cmd_revision, cmd_sql, cmd_status};
7+
use clap::ValueEnum;
78

89
/// vespertide command-line interface.
910
#[derive(Parser, Debug)]
@@ -21,6 +22,14 @@ enum Commands {
2122
Sql,
2223
/// Show SQL per applied migration (chronological log).
2324
Log,
25+
/// Create a new model file from template.
26+
New {
27+
/// Model name (table name).
28+
name: String,
29+
/// Output format: json|yaml|yml (default: json).
30+
#[arg(short = 'f', long = "format", default_value = "json", value_enum)]
31+
format: ModelFormat,
32+
},
2433
/// Show current status.
2534
Status,
2635
/// Create a new revision with a message.
@@ -32,12 +41,20 @@ enum Commands {
3241
Init,
3342
}
3443

44+
#[derive(Copy, Clone, Debug, ValueEnum)]
45+
pub enum ModelFormat {
46+
Json,
47+
Yaml,
48+
Yml,
49+
}
50+
3551
fn main() -> Result<()> {
3652
let cli = Cli::parse();
3753
match cli.command {
3854
Commands::Diff => cmd_diff(),
3955
Commands::Sql => cmd_sql(),
4056
Commands::Log => cmd_log(),
57+
Commands::New { name, format } => cmd_new(name, format),
4158
Commands::Status => cmd_status(),
4259
Commands::Revision { message } => cmd_revision(message),
4360
Commands::Init => cmd_init(),

examples/app/models/user.json

Whitespace-only changes.

0 commit comments

Comments
 (0)