1use anyhow::Result;
2use clap::{ArgAction, Parser, Subcommand, ValueEnum};
3use foundry_core::{apply, doctor, explain, plan, RunOptions};
4use foundry_types::{Archetype, TemplateEngineKind};
5use std::path::PathBuf;
6
7#[derive(Debug, Parser)]
8#[command(name = "foundry")]
9pub struct Cli {
10 #[command(subcommand)]
11 pub command: Command,
12
13 #[arg(long, default_value = ".")]
14 pub project_dir: PathBuf,
15
16 #[arg(long)]
17 pub json: bool,
18
19 #[arg(long)]
20 pub project_name: Option<String>,
21
22 #[arg(long, value_enum)]
23 pub template_engine: Option<TemplateEngineArg>,
24
25 #[arg(long)]
26 pub profile: Option<String>,
27
28 #[arg(long, action = ArgAction::SetTrue, conflicts_with = "skip_post_gen_checks")]
29 pub run_post_gen_checks: bool,
30
31 #[arg(long, action = ArgAction::SetTrue, conflicts_with = "run_post_gen_checks")]
32 pub skip_post_gen_checks: bool,
33}
34
35#[derive(Debug, Clone, ValueEnum)]
36pub enum ArchetypeArg {
37 Web,
38 Tui,
39 Tooling,
40}
41
42impl From<ArchetypeArg> for Archetype {
43 fn from(value: ArchetypeArg) -> Self {
44 match value {
45 ArchetypeArg::Web => Archetype::Web,
46 ArchetypeArg::Tui => Archetype::Tui,
47 ArchetypeArg::Tooling => Archetype::Tooling,
48 }
49 }
50}
51
52#[derive(Debug, Clone, ValueEnum)]
53pub enum TemplateEngineArg {
54 Handlebars,
55 MiniJinja,
56}
57
58impl From<TemplateEngineArg> for TemplateEngineKind {
59 fn from(value: TemplateEngineArg) -> Self {
60 match value {
61 TemplateEngineArg::Handlebars => TemplateEngineKind::Handlebars,
62 TemplateEngineArg::MiniJinja => TemplateEngineKind::MiniJinja,
63 }
64 }
65}
66
67#[derive(Debug, Subcommand)]
68pub enum Command {
69 Plan {
70 #[arg(long)]
71 archetype: Option<ArchetypeArg>,
72 },
73 Doctor {
74 #[arg(long)]
75 archetype: Option<ArchetypeArg>,
76 },
77 Explain {
78 #[arg(long)]
79 archetype: Option<ArchetypeArg>,
80 },
81 Apply {
82 #[arg(long)]
83 archetype: Option<ArchetypeArg>,
84 #[arg(long)]
85 force: bool,
86 },
87}
88
89pub fn run(cli: Cli) -> Result<String> {
90 let (command_name, archetype, force) = match &cli.command {
91 Command::Plan { archetype } => ("plan", archetype.clone(), false),
92 Command::Doctor { archetype } => ("doctor", archetype.clone(), false),
93 Command::Explain { archetype } => ("explain", archetype.clone(), false),
94 Command::Apply { archetype, force } => ("apply", archetype.clone(), *force),
95 };
96
97 let run_post_gen_checks_override = if cli.run_post_gen_checks {
98 Some(true)
99 } else if cli.skip_post_gen_checks {
100 Some(false)
101 } else {
102 None
103 };
104
105 let opts = RunOptions {
106 project_dir: cli.project_dir,
107 archetype_override: archetype.map(Into::into),
108 project_name_override: cli.project_name,
109 template_engine_override: cli.template_engine.map(Into::into),
110 profile_override: cli.profile,
111 run_post_gen_checks_override,
112 force,
113 };
114
115 let env = match command_name {
116 "plan" => plan(&opts)?,
117 "doctor" => doctor(&opts)?,
118 "explain" => explain(&opts)?,
119 "apply" => apply(&opts)?,
120 _ => unreachable!(),
121 };
122
123 if cli.json {
124 Ok(serde_json::to_string_pretty(&env)?)
125 } else {
126 Ok(format!("{}: {}", env.command, env.status))
127 }
128}