Skip to main content

rust_foundry/
lib.rs

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}