1use std::path::PathBuf;
2
3use anyhow::Result;
4use clap::{Args, Subcommand, ValueEnum};
5
6#[derive(Debug, Args)]
7pub struct WizardArgs {
8 #[arg(
9 long,
10 global = true,
11 default_value_t = false,
12 help = "cli.option.schema",
13 long_help = "cli.option.schema.long"
14 )]
15 pub schema: bool,
16 #[command(subcommand)]
17 pub command: Option<WizardCommand>,
18}
19
20#[derive(Debug, Subcommand)]
21pub enum WizardCommand {
22 #[command(about = "cli.wizard.run.about")]
23 Run(WizardRunArgs),
24 #[command(about = "cli.wizard.validate.about")]
25 Validate(WizardValidateArgs),
26 #[command(about = "cli.wizard.apply.about")]
27 Apply(WizardApplyArgs),
28}
29
30#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
31pub enum WizardMode {
32 Create,
33 Update,
34 Doctor,
35}
36
37#[derive(Debug, Args, Default)]
38pub struct WizardRunArgs {
39 #[arg(long, value_name = "FILE", help = "cli.option.answers")]
40 pub answers: Option<PathBuf>,
41 #[arg(
42 long = "emit-answers",
43 value_name = "FILE",
44 help = "cli.option.emit_answers"
45 )]
46 pub emit_answers: Option<PathBuf>,
47 #[arg(
48 long = "schema-version",
49 value_name = "VER",
50 help = "cli.option.schema_version"
51 )]
52 pub schema_version: Option<String>,
53 #[arg(long, default_value_t = false, help = "cli.option.migrate")]
54 pub migrate: bool,
55 #[arg(long, default_value_t = false, help = "cli.option.dry_run")]
56 pub dry_run: bool,
57 #[arg(long, value_enum, help = "cli.wizard.mode.option")]
58 pub mode: Option<WizardMode>,
59 #[arg(long = "env", short = 'e', default_value = "local")]
60 pub env: String,
61}
62
63#[derive(Debug, Args)]
64pub struct WizardValidateArgs {
65 #[arg(long, value_name = "FILE", help = "cli.option.answers")]
66 pub answers: PathBuf,
67 #[arg(
68 long = "emit-answers",
69 value_name = "FILE",
70 help = "cli.option.emit_answers"
71 )]
72 pub emit_answers: Option<PathBuf>,
73 #[arg(
74 long = "schema-version",
75 value_name = "VER",
76 help = "cli.option.schema_version"
77 )]
78 pub schema_version: Option<String>,
79 #[arg(long, default_value_t = false, help = "cli.option.migrate")]
80 pub migrate: bool,
81 #[arg(long, value_enum, help = "cli.wizard.mode.option")]
82 pub mode: Option<WizardMode>,
83 #[arg(long = "env", short = 'e', default_value = "local")]
84 pub env: String,
85}
86
87#[derive(Debug, Args)]
88pub struct WizardApplyArgs {
89 #[arg(long, value_name = "FILE", help = "cli.option.answers")]
90 pub answers: PathBuf,
91 #[arg(
92 long = "emit-answers",
93 value_name = "FILE",
94 help = "cli.option.emit_answers"
95 )]
96 pub emit_answers: Option<PathBuf>,
97 #[arg(
98 long = "schema-version",
99 value_name = "VER",
100 help = "cli.option.schema_version"
101 )]
102 pub schema_version: Option<String>,
103 #[arg(long, default_value_t = false, help = "cli.option.migrate")]
104 pub migrate: bool,
105 #[arg(long, default_value_t = false, help = "cli.option.dry_run")]
106 pub dry_run: bool,
107 #[arg(long, value_enum, help = "cli.wizard.mode.option")]
108 pub mode: Option<WizardMode>,
109 #[arg(long = "env", short = 'e', default_value = "local")]
110 pub env: String,
111}
112
113pub fn run(args: WizardArgs) -> Result<()> {
114 if args.schema {
115 let schema =
116 crate::wizard::answer_document_schema(args.schema_mode(), args.schema_version())?;
117 println!("{}", serde_json::to_string_pretty(&schema)?);
118 return Ok(());
119 }
120
121 match args.command {
122 None => {
123 let zero_action = embedded_root_zero_action();
124 loop {
125 let result = match crate::wizard::run_interactive_with_zero_action(
126 None,
127 None,
128 None,
129 crate::wizard::ExecutionMode::Execute,
130 zero_action,
131 "local",
132 ) {
133 Ok(result) => result,
134 Err(error) if error.to_string() == crate::i18n::tr("wizard.exit.message") => {
135 return Ok(());
136 }
137 Err(error) => return Err(error),
138 };
139 let Some(result) = result else {
140 return Ok(());
141 };
142 crate::wizard::print_plan(&result.plan)?;
143 }
144 }
145 Some(WizardCommand::Run(args)) => crate::wizard::run_command(args),
146 Some(WizardCommand::Validate(args)) => crate::wizard::validate_command(args),
147 Some(WizardCommand::Apply(args)) => crate::wizard::apply_command(args),
148 }
149}
150
151impl WizardArgs {
152 fn schema_mode(&self) -> Option<WizardMode> {
153 match self.command.as_ref() {
154 Some(WizardCommand::Run(args)) => args.mode,
155 Some(WizardCommand::Validate(args)) => args.mode,
156 Some(WizardCommand::Apply(args)) => args.mode,
157 None => None,
158 }
159 }
160
161 fn schema_version(&self) -> Option<&str> {
162 match self.command.as_ref() {
163 Some(WizardCommand::Run(args)) => args.schema_version.as_deref(),
164 Some(WizardCommand::Validate(args)) => args.schema_version.as_deref(),
165 Some(WizardCommand::Apply(args)) => args.schema_version.as_deref(),
166 None => None,
167 }
168 }
169}
170
171fn embedded_root_zero_action() -> crate::wizard::RootMenuZeroAction {
172 match std::env::var("GREENTIC_WIZARD_ROOT_ZERO_ACTION")
173 .ok()
174 .as_deref()
175 {
176 Some("back") => crate::wizard::RootMenuZeroAction::Back,
177 _ => crate::wizard::RootMenuZeroAction::Exit,
178 }
179}