1use std::str::FromStr;
2
3use clap::{error::ErrorKind, CommandFactory, FromArgMatches, Parser};
4
5use crate::config;
6
7pub mod cache;
8pub mod cfg;
9pub mod completion;
10pub mod container;
11pub mod contract;
12pub mod doctor;
13pub mod env;
14pub mod events;
15pub mod fee_stats;
16pub mod fees;
17pub mod global;
18pub mod keys;
19pub mod ledger;
20pub mod message;
21pub mod network;
22pub mod plugin;
23pub mod snapshot;
24pub mod tx;
25pub mod version;
26
27pub mod txn_result;
28
29pub const HEADING_RPC: &str = "Options (RPC)";
30pub const HEADING_ARCHIVE: &str = "Options (Archive)";
31pub const HEADING_GLOBAL: &str = "Options (Global)";
32const ABOUT: &str =
33 "Work seamlessly with Stellar accounts, contracts, and assets from the command line.
34
35- Generate and manage keys and accounts
36- Build, deploy, and interact with contracts
37- Deploy asset contracts
38- Stream events
39- Start local testnets
40- Decode, encode XDR
41- More!
42
43For additional information see:
44
45- Stellar Docs: https://developers.stellar.org
46- Smart Contract Docs: https://developers.stellar.org/docs/build/smart-contracts/overview
47- CLI Docs: https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli";
48
49const LONG_ABOUT: &str = "
51
52To get started generate a new identity:
53
54 stellar keys generate alice
55
56Use keys with the `--source` flag in other commands.
57
58Commands that work with contracts are organized under the `contract` subcommand. List them:
59
60 stellar contract --help
61
62Use contracts like a CLI:
63
64 stellar contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- --help
65
66Anything 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:
67
68 stellar contract invoke --id CCR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OTE2 --source alice --network testnet -- hello --to world
69";
70
71#[derive(Parser, Debug)]
72#[command(
73 name = "stellar",
74 about = ABOUT,
75 version = version::long(),
76 long_about = ABOUT.to_string() + LONG_ABOUT,
77 disable_help_subcommand = true,
78)]
79pub struct Root {
80 #[clap(flatten)]
81 pub global_args: global::Args,
82
83 #[command(subcommand)]
84 pub cmd: Cmd,
85}
86
87impl Root {
88 pub fn new() -> Result<Self, Error> {
89 Self::try_parse().map_err(|e| match e.kind() {
90 ErrorKind::InvalidSubcommand => match plugin::default::run() {
91 Ok(()) => Error::Clap(e),
92 Err(e) => Error::PluginDefault(e),
93 },
94 _ => Error::Clap(e),
95 })
96 }
97
98 pub fn from_arg_matches<I, T>(itr: I) -> Result<Self, clap::Error>
99 where
100 I: IntoIterator<Item = T>,
101 T: Into<std::ffi::OsString> + Clone,
102 {
103 Self::from_arg_matches_mut(&mut Self::command().get_matches_from(itr))
104 }
105
106 pub async fn run(&mut self) -> Result<(), Error> {
107 match &mut self.cmd {
108 Cmd::Completion(completion) => completion.run(),
109 Cmd::Plugin(plugin) => plugin.run(&self.global_args).await?,
110 Cmd::Contract(contract) => contract.run(&self.global_args).await?,
111 Cmd::Doctor(doctor) => doctor.run(&self.global_args).await?,
112 Cmd::Config(config) => config.run()?,
113 Cmd::Events(events) => events.run().await?,
114 Cmd::Xdr(xdr) => xdr.run()?,
115 Cmd::Strkey(strkey) => strkey.run()?,
116 Cmd::Network(network) => network.run(&self.global_args).await?,
117 Cmd::Container(container) => container.run(&self.global_args).await?,
118 Cmd::Snapshot(snapshot) => snapshot.run(&self.global_args).await?,
119 Cmd::Version(version) => version.run(),
120 Cmd::Keys(id) => id.run(&self.global_args).await?,
121 Cmd::Tx(tx) => tx.run(&self.global_args).await?,
122 Cmd::Ledger(ledger) => ledger.run(&self.global_args).await?,
123 Cmd::Message(message) => message.run(&self.global_args).await?,
124 Cmd::Cache(cache) => cache.run()?,
125 Cmd::Env(env) => env.run(&self.global_args)?,
126 Cmd::Fees(env) => env.run(&self.global_args).await?,
127 Cmd::FeeStats(env) => env.run(&self.global_args).await?,
128 }
129 Ok(())
130 }
131}
132
133impl FromStr for Root {
134 type Err = clap::Error;
135
136 fn from_str(s: &str) -> Result<Self, Self::Err> {
137 Self::from_arg_matches(s.split_whitespace())
138 }
139}
140
141#[derive(Parser, Debug)]
142pub enum Cmd {
143 #[command(subcommand)]
145 Contract(contract::Cmd),
146
147 Doctor(doctor::Cmd),
149
150 Events(events::Cmd),
152
153 Env(env::Cmd),
162
163 #[command(subcommand)]
165 Keys(keys::Cmd),
166
167 #[command(subcommand)]
169 Network(network::Cmd),
170
171 #[command(subcommand)]
173 Container(container::Cmd),
174
175 #[command(subcommand)]
177 Config(cfg::Cmd),
178
179 #[command(subcommand)]
181 Snapshot(snapshot::Cmd),
182
183 #[command(subcommand)]
185 Tx(tx::Cmd),
186
187 Xdr(stellar_xdr::cli::Root),
189
190 Strkey(stellar_strkey::cli::Root),
192
193 #[command(long_about = completion::LONG_ABOUT)]
195 Completion(completion::Cmd),
196
197 #[command(subcommand)]
199 Cache(cache::Cmd),
200
201 Version(version::Cmd),
203
204 #[command(subcommand)]
206 Plugin(plugin::Cmd),
207
208 #[command(subcommand)]
210 Ledger(ledger::Cmd),
211
212 #[command(subcommand)]
214 Message(message::Cmd),
215
216 FeeStats(fee_stats::Cmd),
218
219 #[command(subcommand)]
221 Fees(fees::Cmd),
222}
223
224#[derive(thiserror::Error, Debug)]
225pub enum Error {
226 #[error(transparent)]
228 Contract(#[from] contract::Error),
229
230 #[error(transparent)]
231 Doctor(#[from] doctor::Error),
232
233 #[error(transparent)]
234 Events(#[from] events::Error),
235
236 #[error(transparent)]
237 Keys(#[from] keys::Error),
238
239 #[error(transparent)]
240 Xdr(#[from] stellar_xdr::cli::Error),
241
242 #[error(transparent)]
243 Strkey(#[from] stellar_strkey::cli::Error),
244
245 #[error(transparent)]
246 Clap(#[from] clap::error::Error),
247
248 #[error(transparent)]
249 Plugin(#[from] plugin::Error),
250
251 #[error(transparent)]
252 PluginDefault(#[from] plugin::default::Error),
253
254 #[error(transparent)]
255 Network(#[from] network::Error),
256
257 #[error(transparent)]
258 Container(#[from] container::Error),
259
260 #[error(transparent)]
261 Config(#[from] cfg::Error),
262
263 #[error(transparent)]
264 Snapshot(#[from] snapshot::Error),
265
266 #[error(transparent)]
267 Tx(#[from] tx::Error),
268
269 #[error(transparent)]
270 Cache(#[from] cache::Error),
271
272 #[error(transparent)]
273 Env(#[from] env::Error),
274
275 #[error(transparent)]
276 Ledger(#[from] ledger::Error),
277
278 #[error(transparent)]
279 Message(#[from] message::Error),
280
281 #[error(transparent)]
282 FeeStats(#[from] fee_stats::Error),
283
284 #[error(transparent)]
285 Fees(#[from] fees::Error),
286}