grex_cli/cli/verbs/
init.rs1use crate::cli::args::{GlobalFlags, InitArgs};
8use anyhow::Result;
9use std::path::{Path, PathBuf};
10use tokio_util::sync::CancellationToken;
11
12fn derive_pack_name(dir: &Path) -> String {
17 let raw = dir.file_name().and_then(|s| s.to_str()).unwrap_or("workspace");
18 let mut out = String::with_capacity(raw.len());
19 for ch in raw.chars() {
20 let c = ch.to_ascii_lowercase();
21 if c.is_ascii_lowercase() || c.is_ascii_digit() {
22 out.push(c);
23 } else {
24 out.push('-');
25 }
26 }
27 while !out.is_empty() && !out.chars().next().unwrap_or('-').is_ascii_lowercase() {
28 out.remove(0);
29 }
30 if out.is_empty() {
31 "workspace".to_string()
32 } else {
33 out
34 }
35}
36
37fn minimal_pack_yaml(name: &str) -> String {
38 format!("schema_version: \"1\"\nname: {name}\ntype: meta\nactions: []\nchildren: []\n")
39}
40
41pub fn run(args: InitArgs, global: &GlobalFlags, _cancel: &CancellationToken) -> Result<()> {
42 match run_impl(args.path, global.json) {
43 Outcome::Ok => Ok(()),
44 Outcome::AlreadyInitialized => std::process::exit(1),
45 Outcome::Io => std::process::exit(2),
46 }
47}
48
49enum Outcome {
50 Ok,
51 AlreadyInitialized,
52 Io,
53}
54
55fn run_impl(path: Option<PathBuf>, json: bool) -> Outcome {
56 let dir = match path {
57 Some(p) => p,
58 None => match std::env::current_dir() {
59 Ok(cwd) => cwd,
60 Err(err) => {
61 emit_error(json, "io", &format!("resolve cwd: {err}"));
62 return Outcome::Io;
63 }
64 },
65 };
66
67 if let Err(err) = std::fs::create_dir_all(&dir) {
68 emit_error(json, "io", &format!("create workspace dir {}: {err}", dir.display()));
69 return Outcome::Io;
70 }
71
72 let grex_dir = dir.join(".grex");
73 let manifest_path = grex_dir.join("pack.yaml");
74 if manifest_path.exists() {
75 emit_error(json, "already_initialized", &format!("{} already initialized", dir.display()));
76 return Outcome::AlreadyInitialized;
77 }
78
79 if let Err(err) = std::fs::create_dir_all(&grex_dir) {
80 emit_error(json, "io", &format!("create {}: {err}", grex_dir.display()));
81 return Outcome::Io;
82 }
83
84 let name = derive_pack_name(&dir);
85 if let Err(err) = std::fs::write(&manifest_path, minimal_pack_yaml(&name)) {
86 emit_error(json, "io", &format!("write {}: {err}", manifest_path.display()));
87 return Outcome::Io;
88 }
89
90 emit_ok(json, &dir, &manifest_path);
91 Outcome::Ok
92}
93
94fn emit_ok(json: bool, dir: &Path, manifest: &Path) {
95 if json {
96 let doc = serde_json::json!({
97 "verb": "init",
98 "status": "ok",
99 "path": dir.display().to_string(),
100 "manifest": manifest.display().to_string(),
101 });
102 println!("{}", serde_json::to_string(&doc).unwrap_or_default());
103 } else {
104 println!("grex init: wrote {}", manifest.display());
105 }
106}
107
108fn emit_error(json: bool, kind: &str, msg: &str) {
109 if json {
110 let doc = serde_json::json!({
111 "verb": "init",
112 "error": { "kind": kind, "message": msg },
113 });
114 println!("{}", serde_json::to_string(&doc).unwrap_or_default());
115 } else {
116 eprintln!("grex init: {msg}");
117 }
118}