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