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}
60
61#[derive(Debug, Args)]
62pub struct WizardValidateArgs {
63 #[arg(long, value_name = "FILE", help = "cli.option.answers")]
64 pub answers: PathBuf,
65 #[arg(
66 long = "emit-answers",
67 value_name = "FILE",
68 help = "cli.option.emit_answers"
69 )]
70 pub emit_answers: Option<PathBuf>,
71 #[arg(
72 long = "schema-version",
73 value_name = "VER",
74 help = "cli.option.schema_version"
75 )]
76 pub schema_version: Option<String>,
77 #[arg(long, default_value_t = false, help = "cli.option.migrate")]
78 pub migrate: bool,
79 #[arg(long, value_enum, help = "cli.wizard.mode.option")]
80 pub mode: Option<WizardMode>,
81}
82
83#[derive(Debug, Args)]
84pub struct WizardApplyArgs {
85 #[arg(long, value_name = "FILE", help = "cli.option.answers")]
86 pub answers: PathBuf,
87 #[arg(
88 long = "emit-answers",
89 value_name = "FILE",
90 help = "cli.option.emit_answers"
91 )]
92 pub emit_answers: Option<PathBuf>,
93 #[arg(
94 long = "schema-version",
95 value_name = "VER",
96 help = "cli.option.schema_version"
97 )]
98 pub schema_version: Option<String>,
99 #[arg(long, default_value_t = false, help = "cli.option.migrate")]
100 pub migrate: bool,
101 #[arg(long, default_value_t = false, help = "cli.option.dry_run")]
102 pub dry_run: bool,
103 #[arg(long, value_enum, help = "cli.wizard.mode.option")]
104 pub mode: Option<WizardMode>,
105}
106
107pub fn run(args: WizardArgs) -> Result<()> {
108 if args.schema {
109 let schema =
110 crate::wizard::answer_document_schema(args.schema_mode(), args.schema_version())?;
111 println!("{}", serde_json::to_string_pretty(&schema)?);
112 return Ok(());
113 }
114
115 match args.command {
116 None => {
117 let zero_action = embedded_root_zero_action();
118 loop {
119 let result = match crate::wizard::run_interactive_with_zero_action(
120 None,
121 None,
122 None,
123 crate::wizard::ExecutionMode::Execute,
124 zero_action,
125 ) {
126 Ok(result) => result,
127 Err(error) if error.to_string() == crate::i18n::tr("wizard.exit.message") => {
128 return Ok(());
129 }
130 Err(error) => return Err(error),
131 };
132 let Some(result) = result else {
133 return Ok(());
134 };
135 crate::wizard::print_plan(&result.plan)?;
136 }
137 }
138 Some(WizardCommand::Run(args)) => crate::wizard::run_command(args),
139 Some(WizardCommand::Validate(args)) => crate::wizard::validate_command(args),
140 Some(WizardCommand::Apply(args)) => crate::wizard::apply_command(args),
141 }
142}
143
144impl WizardArgs {
145 fn schema_mode(&self) -> Option<WizardMode> {
146 match self.command.as_ref() {
147 Some(WizardCommand::Run(args)) => args.mode,
148 Some(WizardCommand::Validate(args)) => args.mode,
149 Some(WizardCommand::Apply(args)) => args.mode,
150 None => None,
151 }
152 }
153
154 fn schema_version(&self) -> Option<&str> {
155 match self.command.as_ref() {
156 Some(WizardCommand::Run(args)) => args.schema_version.as_deref(),
157 Some(WizardCommand::Validate(args)) => args.schema_version.as_deref(),
158 Some(WizardCommand::Apply(args)) => args.schema_version.as_deref(),
159 None => None,
160 }
161 }
162}
163
164fn embedded_root_zero_action() -> crate::wizard::RootMenuZeroAction {
165 match std::env::var("GREENTIC_WIZARD_ROOT_ZERO_ACTION")
166 .ok()
167 .as_deref()
168 {
169 Some("back") => crate::wizard::RootMenuZeroAction::Back,
170 _ => crate::wizard::RootMenuZeroAction::Exit,
171 }
172}