Skip to main content

stellar_scaffold_cli/commands/
mod.rs

1use std::str::FromStr;
2
3use clap::{CommandFactory, FromArgMatches, Parser, command};
4use stellar_cli;
5
6pub mod build;
7pub mod clean;
8pub mod ext;
9pub mod generate;
10pub mod init;
11pub mod update_env;
12pub mod upgrade;
13pub mod version;
14pub mod watch;
15
16const ABOUT: &str = "Build smart contracts with frontend support";
17
18#[derive(Parser, Debug)]
19#[command(
20    name = "stellar-scaffold",
21    about = ABOUT,
22    disable_help_subcommand = true,
23)]
24pub struct Root {
25    #[clap(flatten)]
26    pub global_args: stellar_cli::commands::global::Args,
27
28    #[command(subcommand)]
29    pub cmd: Cmd,
30}
31
32impl Root {
33    pub fn new() -> Result<Self, clap::Error> {
34        let mut matches = Self::command().get_matches();
35        Self::from_arg_matches_mut(&mut matches)
36    }
37
38    pub fn from_arg_matches<I, T>(itr: I) -> Result<Self, clap::Error>
39    where
40        I: IntoIterator<Item = T>,
41        T: Into<std::ffi::OsString> + Clone,
42    {
43        Self::from_arg_matches_mut(&mut Self::command().get_matches_from(itr))
44    }
45    pub async fn run(&mut self) -> Result<(), Error> {
46        match &mut self.cmd {
47            Cmd::Init(init_info) => init_info.run(&self.global_args).await?,
48            Cmd::Version(version_info) => version_info.run(),
49            Cmd::Build(build_info) => build_info.run(&self.global_args).await?,
50            Cmd::Generate(generate) => match &mut generate.cmd {
51                generate::Command::Contract(contract) => contract.run(&self.global_args).await?,
52            },
53            Cmd::Ext(ext_cmd) => match &ext_cmd.cmd {
54                ext::Command::Ls(ls) => ls.run(&self.global_args).map_err(ext::Error::from)?,
55            },
56            Cmd::Upgrade(upgrade_info) => upgrade_info.run(&self.global_args).await?,
57            Cmd::UpdateEnv(e) => e.run()?,
58            Cmd::Watch(watch_info) => watch_info.run(&self.global_args).await?,
59            Cmd::Clean(clean) => clean.run(&self.global_args)?,
60        }
61        Ok(())
62    }
63}
64
65impl FromStr for Root {
66    type Err = clap::Error;
67
68    fn from_str(s: &str) -> Result<Self, Self::Err> {
69        Self::from_arg_matches(s.split_whitespace())
70    }
71}
72
73#[derive(Parser, Debug)]
74pub enum Cmd {
75    /// Initialize the project
76    Init(init::Cmd),
77    /// Version of the scaffold-stellar-cli
78    Version(version::Cmd),
79
80    /// Build contracts, resolving dependencies in the correct order. If you have an `environments.toml` file, it will also follow its instructions to configure the environment set by the `STELLAR_SCAFFOLD_ENV` environment variable, turning your contracts into frontend packages (NPM dependencies).
81    Build(build::Command),
82
83    /// generate contracts
84    Generate(generate::Cmd),
85
86    /// Inspect and manage extensions
87    Ext(ext::Cmd),
88
89    /// Upgrade an existing Soroban workspace to a scaffold project
90    Upgrade(upgrade::Cmd),
91
92    /// Update an environment variable in a .env file
93    UpdateEnv(update_env::Cmd),
94
95    /// Monitor contracts and environments.toml for changes and rebuild as needed
96    Watch(watch::Cmd),
97
98    /// Clean Scaffold-generated artifacts from the given workspace
99    Clean(clean::Cmd),
100}
101
102#[derive(thiserror::Error, Debug)]
103pub enum Error {
104    // TODO: stop using Debug for displaying errors
105    #[error(transparent)]
106    Init(#[from] init::Error),
107    #[error(transparent)]
108    BuildContracts(#[from] build::Error),
109    #[error(transparent)]
110    Contract(#[from] generate::contract::Error),
111    #[error(transparent)]
112    Ext(#[from] ext::Error),
113    #[error(transparent)]
114    Upgrade(#[from] upgrade::Error),
115    #[error(transparent)]
116    UpdateEnv(#[from] update_env::Error),
117    #[error(transparent)]
118    Watch(#[from] watch::Error),
119    #[error(transparent)]
120    Clean(#[from] clean::Error),
121}
122
123pub fn npm_cmd() -> &'static str {
124    if cfg!(target_os = "windows") {
125        "npm.cmd"
126    } else {
127        "npm"
128    }
129}