greentic_bundle/cli/
init.rs1use std::path::PathBuf;
2
3use anyhow::Result;
4use clap::Args;
5use serde::Serialize;
6
7#[derive(Debug, Args, Default)]
8pub struct InitArgs {
9 #[arg(value_name = "PATH", help = "cli.init.path.option")]
10 pub path: Option<PathBuf>,
11
12 #[arg(long, value_name = "NAME", help = "cli.init.bundle_name.option")]
13 pub bundle_name: Option<String>,
14
15 #[arg(long, value_name = "ID", help = "cli.init.bundle_id.option")]
16 pub bundle_id: Option<String>,
17
18 #[arg(
19 long,
20 value_name = "MODE",
21 default_value = "create",
22 help = "cli.init.mode.option"
23 )]
24 pub mode: String,
25
26 #[arg(
27 long,
28 value_name = "LOCALE",
29 default_value = "en",
30 help = "cli.init.locale.option"
31 )]
32 pub locale: String,
33
34 #[arg(long, default_value_t = false, help = "cli.option.execute")]
35 pub execute: bool,
36}
37
38#[derive(Debug, Serialize)]
39struct InitPreview {
40 root: String,
41 execute: bool,
42 bundle_id: String,
43 bundle_name: String,
44 mode: String,
45 locale: String,
46 expected_file_writes: Vec<String>,
47}
48
49pub fn run(args: InitArgs) -> Result<()> {
50 let root = args.path.unwrap_or_else(|| PathBuf::from("."));
51 let bundle_name = args
52 .bundle_name
53 .unwrap_or_else(|| default_bundle_name(&root));
54 let bundle_id = args
55 .bundle_id
56 .unwrap_or_else(|| normalize_bundle_id(&bundle_name));
57 let workspace = crate::project::BundleWorkspaceDefinition::new(
58 bundle_name.clone(),
59 bundle_id.clone(),
60 args.locale.clone(),
61 args.mode.clone(),
62 );
63 let preview = InitPreview {
64 root: root.display().to_string(),
65 execute: args.execute,
66 bundle_id,
67 bundle_name,
68 mode: args.mode,
69 locale: args.locale,
70 expected_file_writes: vec![
71 root.join(crate::project::WORKSPACE_ROOT_FILE)
72 .display()
73 .to_string(),
74 root.join(crate::project::LOCK_FILE).display().to_string(),
75 root.join("tenants/default/tenant.gmap")
76 .display()
77 .to_string(),
78 root.join("resolved/default.yaml").display().to_string(),
79 root.join("state/resolved/default.yaml")
80 .display()
81 .to_string(),
82 ],
83 };
84 if args.execute {
85 crate::project::init_bundle_workspace(&root, &workspace)?;
86 }
87 println!("{}", serde_json::to_string_pretty(&preview)?);
88 Ok(())
89}
90
91fn default_bundle_name(root: &std::path::Path) -> String {
92 root.file_name()
93 .and_then(|name| name.to_str())
94 .filter(|name| !name.is_empty() && *name != ".")
95 .map(|name| name.replace('-', " "))
96 .unwrap_or_else(|| "bundle".to_string())
97}
98
99fn normalize_bundle_id(raw: &str) -> String {
100 let normalized = raw
101 .trim()
102 .to_ascii_lowercase()
103 .chars()
104 .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '-' })
105 .collect::<String>();
106 let trimmed = normalized.trim_matches('-').to_string();
107 if trimmed.is_empty() {
108 "bundle".to_string()
109 } else {
110 trimmed
111 }
112}