1#![doc = include_str!("../README.md")]
2
3use std::sync::Arc;
4
5use benchmark::BenchmarkSubCommand;
6use blob::BlobSubCommand;
7use blober::BloberSubCommand;
8use clap::{CommandFactory, Parser, Subcommand, error::ErrorKind};
9use data_anchor_client::{DataAnchorClient, DataAnchorClientResult};
10use formatting::OutputFormat;
11use indexer::IndexerSubCommand;
12use solana_cli_config::Config;
13use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::EncodableKey};
14use tracing::trace;
15
16mod benchmark;
17mod blob;
18mod blober;
19mod formatting;
20mod indexer;
21
22#[derive(Debug, Parser)]
24#[command(version, about, long_about)]
25struct Cli {
26 #[command(subcommand)]
27 pub command: Command,
28
29 #[arg(short, long, global = true, env = "DATA_ANCHOR_PROGRAM_ID")]
31 pub program_id: Option<Pubkey>,
32
33 #[arg(short, long, global = true, env = "DATA_ANCHOR_NAMESPACE")]
35 pub namespace: Option<String>,
36
37 #[arg(short = 's', long, global = true, env = "DATA_ANCHOR_PAYER")]
39 pub payer: Option<String>,
40
41 #[arg(
43 short,
44 long,
45 global = true,
46 env = "DATA_ANCHOR_OUTPUT",
47 value_enum,
48 default_value_t = OutputFormat::Text
49 )]
50 pub output: OutputFormat,
51
52 #[arg(short, long, global = true, env = "DATA_ANCHOR_INDEXER_URL")]
54 pub indexer_url: Option<String>,
55
56 #[arg(
58 long,
59 global = true,
60 env = "DATA_ANCHOR_INDEXER_API_TOKEN",
61 hide_env_values = true
62 )]
63 pub indexer_api_token: Option<String>,
64
65 #[arg(
67 short,
68 long,
69 global = true,
70 env = "DATA_ANCHOR_SOLANA_CONFIG_FILE",
71 default_value_t = solana_cli_config::CONFIG_FILE.as_ref().unwrap().clone()
72 )]
73 pub config_file: String,
74}
75
76impl Cli {
77 fn exit_with_missing_arg(msg: &str) -> ! {
78 Self::command()
79 .error(ErrorKind::MissingRequiredArgument, msg)
80 .exit()
81 }
82}
83
84#[derive(Debug, Subcommand)]
85enum Command {
86 #[command(subcommand, visible_alias = "br")]
88 Blober(BloberSubCommand),
89 #[command(subcommand, visible_alias = "b")]
91 Blob(BlobSubCommand),
92 #[command(subcommand, visible_alias = "i")]
94 Indexer(IndexerSubCommand),
95 #[command(subcommand, visible_alias = "m")]
97 Benchmark(BenchmarkSubCommand),
98}
99
100pub struct Options {
101 command: Command,
102 program_id: Pubkey,
103 payer: Arc<Keypair>,
104 indexer_url: Option<String>,
105 indexer_api_token: Option<String>,
106 namespace: String,
107 config: Config,
108 output: OutputFormat,
109}
110
111impl Options {
112 pub fn parse() -> Self {
115 trace!("Parsing options");
116 let args = Cli::parse();
117 let config = Config::load(&args.config_file).unwrap();
118 let payer_path = args.payer.as_ref().unwrap_or(&config.keypair_path);
119 let payer = Arc::new(Keypair::read_from_file(payer_path).unwrap());
120 trace!("Parsed options: {args:?} {config:?} {payer:?}");
121
122 let Some(namespace) = args.namespace else {
123 Cli::exit_with_missing_arg(
124 "Namespace is not set. Please provide a namespace using the --namespace flag or set the DATA_ANCHOR_NAMESPACE environment variable.",
125 );
126 };
127
128 let Some(program_id) = args.program_id else {
129 Cli::exit_with_missing_arg(
130 "Program ID is not set. Please provide a program ID using the --program-id flag or set the DATA_ANCHOR_PROGRAM_ID environment variable.",
131 );
132 };
133
134 Self {
135 namespace,
136 indexer_url: args.indexer_url,
137 indexer_api_token: args.indexer_api_token,
138 command: args.command,
139 program_id,
140 output: args.output,
141 payer,
142 config,
143 }
144 }
145
146 pub async fn run(self) -> DataAnchorClientResult {
148 let builder = DataAnchorClient::builder()
149 .payer(self.payer.clone())
150 .program_id(self.program_id);
151
152 let client = if let Some(indexer_url) = self.indexer_url {
153 builder
154 .indexer_from_url(&indexer_url, self.indexer_api_token.clone())
155 .await?
156 .build_with_config(self.config)
157 .await?
158 } else {
159 builder.build_with_config(self.config).await?
160 };
161
162 let client = Arc::new(client);
163
164 let output = match self.command {
165 Command::Blober(subcommand) => subcommand.run(client.clone(), &self.namespace).await,
166 Command::Blob(subcommand) => subcommand.run(client.clone(), &self.namespace).await,
167 Command::Indexer(subcommand) => subcommand.run(client.clone(), &self.namespace).await,
168 Command::Benchmark(subcommand) => subcommand.run(client.clone(), &self.namespace).await,
169 }?;
170
171 println!("{}", output.serialize_output(self.output));
172
173 Ok(())
174 }
175}