soroban_cli/commands/
mod.rs

1use std::str::FromStr;
2
3use async_trait::async_trait;
4use clap::{command, error::ErrorKind, CommandFactory, FromArgMatches, Parser};
5
6use crate::config;
7
8pub mod cache;
9pub mod completion;
10pub mod container;
11pub mod contract;
12pub mod env;
13pub mod events;
14pub mod global;
15pub mod keys;
16pub mod ledger;
17pub mod network;
18pub mod plugin;
19pub mod snapshot;
20pub mod tx;
21pub mod version;
22
23pub mod txn_result;
24
25pub const HEADING_RPC: &str = "Options (RPC)";
26pub const HEADING_GLOBAL: &str = "Options (Global)";
27const ABOUT: &str =
28    "Work seamlessly with Stellar accounts, contracts, and assets from the command line.
29
30- Generate and manage keys and accounts
31- Build, deploy, and interact with contracts
32- Deploy asset contracts
33- Stream events
34- Start local testnets
35- Decode, encode XDR
36- More!
37
38For additional information see:
39
40- Stellar Docs: https://developers.stellar.org
41- Smart Contract Docs: https://developers.stellar.org/docs/build/smart-contracts/overview
42- CLI Docs: https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli";
43
44// long_about is shown when someone uses `--help`; short help when using `-h`
45const LONG_ABOUT: &str = "
46
47To get started generate a new identity:
48
49    stellar keys generate alice
50
51Use keys with the `--source` flag in other commands.
52
53Commands that work with contracts are organized under the `contract` subcommand. List them:
54
55    stellar contract --help
56
57Use contracts like a CLI:
58
59    stellar contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- --help
60
61Anything after the `--` double dash (the \"slop\") is parsed as arguments to the contract-specific CLI, generated on-the-fly from the contract schema. For the hello world example, with a function called `hello` that takes one string argument `to`, here's how you invoke it:
62
63    stellar contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- hello --to world
64";
65
66#[derive(Parser, Debug)]
67#[command(
68    name = "stellar",
69    about = ABOUT,
70    version = version::long(),
71    long_about = ABOUT.to_string() + LONG_ABOUT,
72    disable_help_subcommand = true,
73)]
74pub struct Root {
75    #[clap(flatten)]
76    pub global_args: global::Args,
77
78    #[command(subcommand)]
79    pub cmd: Cmd,
80}
81
82impl Root {
83    pub fn new() -> Result<Self, Error> {
84        Self::try_parse().map_err(|e| {
85            if std::env::args().any(|s| s == "--list") {
86                let _ = plugin::ls::Cmd.run();
87                std::process::exit(0);
88            }
89
90            match e.kind() {
91                ErrorKind::InvalidSubcommand => match plugin::default::run() {
92                    Ok(()) => Error::Clap(e),
93                    Err(e) => Error::PluginDefault(e),
94                },
95                _ => Error::Clap(e),
96            }
97        })
98    }
99
100    pub fn from_arg_matches<I, T>(itr: I) -> Result<Self, clap::Error>
101    where
102        I: IntoIterator<Item = T>,
103        T: Into<std::ffi::OsString> + Clone,
104    {
105        Self::from_arg_matches_mut(&mut Self::command().get_matches_from(itr))
106    }
107
108    pub async fn run(&mut self) -> Result<(), Error> {
109        match &mut self.cmd {
110            Cmd::Completion(completion) => completion.run(),
111            Cmd::Plugin(plugin) => plugin.run(&self.global_args).await?,
112            Cmd::Contract(contract) => contract.run(&self.global_args).await?,
113            Cmd::Events(events) => events.run().await?,
114            Cmd::Xdr(xdr) => xdr.run()?,
115            Cmd::Network(network) => network.run(&self.global_args).await?,
116            Cmd::Container(container) => container.run(&self.global_args).await?,
117            Cmd::Snapshot(snapshot) => snapshot.run(&self.global_args).await?,
118            Cmd::Version(version) => version.run(),
119            Cmd::Keys(id) => id.run(&self.global_args).await?,
120            Cmd::Tx(tx) => tx.run(&self.global_args).await?,
121            Cmd::Cache(cache) => cache.run()?,
122            Cmd::Env(env) => env.run(&self.global_args)?,
123            Cmd::Ledger(env) => env.run(&self.global_args).await?,
124        }
125        Ok(())
126    }
127}
128
129impl FromStr for Root {
130    type Err = clap::Error;
131
132    fn from_str(s: &str) -> Result<Self, Self::Err> {
133        Self::from_arg_matches(s.split_whitespace())
134    }
135}
136
137#[derive(Parser, Debug)]
138pub enum Cmd {
139    /// Tools for smart contract developers
140    #[command(subcommand)]
141    Contract(contract::Cmd),
142
143    /// Watch the network for contract events
144    Events(events::Cmd),
145
146    /// Prints the environment variables
147    ///
148    /// Prints to stdout in a format that can be used as .env file. Environment
149    /// variables have precedence over defaults.
150    ///
151    /// Pass a name to get the value of a single environment variable.
152    ///
153    /// If there are no environment variables in use, prints the defaults.
154    Env(env::Cmd),
155
156    /// Create and manage identities including keys and addresses
157    #[command(subcommand)]
158    Keys(keys::Cmd),
159
160    /// Configure connection to networks
161    #[command(subcommand)]
162    Network(network::Cmd),
163
164    /// Start local networks in containers
165    #[command(subcommand)]
166    Container(container::Cmd),
167
168    /// Download a snapshot of a ledger from an archive.
169    #[command(subcommand)]
170    Snapshot(snapshot::Cmd),
171
172    /// Sign, Simulate, and Send transactions
173    #[command(subcommand)]
174    Tx(tx::Cmd),
175
176    /// Decode and encode XDR
177    Xdr(stellar_xdr::cli::Root),
178
179    /// Print shell completion code for the specified shell.
180    #[command(long_about = completion::LONG_ABOUT)]
181    Completion(completion::Cmd),
182
183    /// Cache for transactions and contract specs
184    #[command(subcommand)]
185    Cache(cache::Cmd),
186
187    /// Print version information
188    Version(version::Cmd),
189
190    /// The subcommand for CLI plugins
191    #[command(subcommand)]
192    Plugin(plugin::Cmd),
193
194    /// Fetch ledger information
195    #[command(subcommand)]
196    Ledger(ledger::Cmd),
197}
198
199#[derive(thiserror::Error, Debug)]
200pub enum Error {
201    // TODO: stop using Debug for displaying errors
202    #[error(transparent)]
203    Contract(#[from] contract::Error),
204
205    #[error(transparent)]
206    Events(#[from] events::Error),
207
208    #[error(transparent)]
209    Keys(#[from] keys::Error),
210
211    #[error(transparent)]
212    Xdr(#[from] stellar_xdr::cli::Error),
213
214    #[error(transparent)]
215    Clap(#[from] clap::error::Error),
216
217    #[error(transparent)]
218    Plugin(#[from] plugin::Error),
219
220    #[error(transparent)]
221    PluginDefault(#[from] plugin::default::Error),
222
223    #[error(transparent)]
224    Network(#[from] network::Error),
225
226    #[error(transparent)]
227    Container(#[from] container::Error),
228
229    #[error(transparent)]
230    Snapshot(#[from] snapshot::Error),
231
232    #[error(transparent)]
233    Tx(#[from] tx::Error),
234
235    #[error(transparent)]
236    Cache(#[from] cache::Error),
237
238    #[error(transparent)]
239    Env(#[from] env::Error),
240
241    #[error(transparent)]
242    Ledger(#[from] ledger::Error),
243}
244
245#[async_trait]
246pub trait NetworkRunnable {
247    type Error;
248    type Result;
249
250    async fn run_against_rpc_server(
251        &self,
252        global_args: Option<&global::Args>,
253        config: Option<&config::Args>,
254    ) -> Result<Self::Result, Self::Error>;
255}