1
use clap::{command, ArgGroup, Args, Parser, Subcommand};
2

            
3
#[derive(Parser, Debug)]
4
#[command(version)]
5
#[command(group(
6
    // Ensures mutual exclusivity of config, or keypair and rpc_url
7
    ArgGroup::new("config_group")
8
        .required(false)
9
        .args(&["config"])
10
        .conflicts_with("rpc_url")
11
        .conflicts_with("keypair")
12
        .multiple(false)
13
))]
14
pub struct BonsolCli {
15
    #[arg(
16
        help = "The path to a Solana CLI config [Default: '~/.config/solana/cli/config.yml']",
17
        short = 'c',
18
        long
19
    )]
20
    pub config: Option<String>,
21

            
22
    #[arg(
23
        help = "The path to a Solana keypair file [Default: '~/.config/solana/id.json']",
24
        short = 'k',
25
        long,
26
        requires = "rpc_url"
27
    )]
28
    pub keypair: Option<String>,
29

            
30
    #[arg(
31
        help = "The Solana cluster the Solana CLI will make requests to",
32
        short = 'u',
33
        long,
34
        requires = "keypair"
35
    )]
36
    pub rpc_url: Option<String>,
37

            
38
    #[command(subcommand)]
39
    pub command: Command,
40
}
41

            
42
#[derive(Debug, Clone, Args)]
43
pub struct S3UploadArgs {
44
    #[arg(
45
        help = "Specify the S3 bucket name",
46
        long,
47
        required = true,
48
        value_parser = |s: &str| {
49
            if s.trim().is_empty() {
50
                anyhow::bail!("expected a non-empty string representation of an S3 bucket name")
51
            }
52
            Ok(s.to_string())
53
        }
54
    )]
55
    pub bucket: String,
56

            
57
    #[arg(
58
        help = "Specify the AWS access key ID",
59
        long,
60
        required = true,
61
        env = "AWS_ACCESS_KEY_ID"
62
    )]
63
    pub access_key: String,
64

            
65
    #[arg(
66
        help = "Specify the AWS secret access key",
67
        long,
68
        required = true,
69
        env = "AWS_SECRET_ACCESS_KEY"
70
    )]
71
    pub secret_key: String,
72

            
73
    #[arg(
74
        help = "Specify the AWS region",
75
        long,
76
        required = true,
77
        env = "AWS_REGION"
78
    )]
79
    pub region: String,
80

            
81
    #[arg(
82
        help = "Specify the AWS S3 compatibility endpoint",
83
        long,
84
        required = false,
85
        env = "AWS_S3_ENDPOINT"
86
    )]
87
    pub endpoint: Option<String>,
88

            
89
    #[command(flatten)]
90
    pub shared_args: SharedDeployArgs,
91
}
92

            
93
#[derive(Debug, Clone, Args)]
94
pub struct UrlUploadArgs {
95
    #[arg(help = "Specify a URL endpoint to deploy to", long, required = true)]
96
    pub url: String,
97

            
98
    #[arg(
99
        long = "no-post",
100
        help = "Skip posting the binary to the URL endpoint and assume it's already there",
101
        action = clap::ArgAction::SetTrue
102
    )]
103
    pub no_post: bool,
104

            
105
    #[command(flatten)]
106
    pub shared_args: SharedDeployArgs,
107
}
108

            
109
#[derive(Debug, Clone, Subcommand)]
110
pub enum DeployArgs {
111
    #[command(about = "Deploy a program using an AWS S3 bucket")]
112
    S3(S3UploadArgs),
113

            
114
    #[command(about = "Deploy a program manually with a URL")]
115
    Url(UrlUploadArgs),
116
}
117

            
118
impl DeployArgs {
119
    pub fn shared_args(&self) -> SharedDeployArgs {
120
        match self {
121
            Self::S3(s3) => s3.shared_args.clone(),
122
            Self::Url(url) => url.shared_args.clone(),
123
        }
124
    }
125
}
126

            
127
#[derive(Debug, Clone, Args)]
128
pub struct SharedDeployArgs {
129
    #[arg(
130
        help = "The path to the program's manifest file (manifest.json)",
131
        short = 'm',
132
        long
133
    )]
134
    pub manifest_path: String,
135

            
136
    #[arg(
137
        help = "Whether to automatically confirm deployment",
138
        short = 'y',
139
        long
140
    )]
141
    pub auto_confirm: bool,
142
}
143

            
144
#[derive(Subcommand, Debug)]
145
pub enum Command {
146
    #[command(
147
        about = "Deploy a program with various storage options, such as S3, or manually with a URL"
148
    )]
149
    Deploy {
150
        #[clap(subcommand)]
151
        deploy_args: DeployArgs,
152
    },
153

            
154
    #[command(about = "Build a ZK program")]
155
    Build {
156
        #[arg(
157
            help = "The path to a ZK program folder containing a Cargo.toml",
158
            short = 'z',
159
            long
160
        )]
161
        zk_program_path: String,
162
    },
163

            
164
    #[command(about = "Estimate the execution cost of a ZK RISC0 program")]
165
    Estimate {
166
        #[arg(
167
            help = "The path to the program's manifest file (manifest.json)",
168
            short = 'm',
169
            long
170
        )]
171
        manifest_path: String,
172

            
173
        #[arg(help = "The path to the program input file", short = 'i', long)]
174
        input_file: Option<String>,
175

            
176
        #[arg(
177
            help = "Set the maximum number of cycles [default: 16777216u64]",
178
            short = 'c',
179
            long
180
        )]
181
        max_cycles: Option<u64>,
182
    },
183

            
184
    Execute {
185
        #[arg(short = 'f', long)]
186
        execution_request_file: Option<String>,
187

            
188
        // overridable settings
189
        #[arg(short = 'p', long)]
190
        program_id: Option<String>,
191

            
192
        #[arg(short = 'e', long)]
193
        execution_id: Option<String>,
194

            
195
        #[arg(short = 'x', long)]
196
        expiry: Option<u64>,
197

            
198
        #[arg(short = 'm', long)]
199
        tip: Option<u64>,
200

            
201
        #[arg(short = 'i', long, help = "override inputs in execution request file")]
202
        input_file: Option<String>,
203

            
204
        /// wait for execution to be proven
205
        #[arg(short = 'w', long, help = "wait for execution to be proven")]
206
        wait: bool,
207

            
208
        /// timeout in seconds
209
        #[arg(short = 't', long, help = "timeout in seconds")]
210
        timeout: Option<u64>,
211
    },
212

            
213
    Prove {
214
        #[arg(short = 'm', long)]
215
        manifest_path: Option<String>,
216

            
217
        #[arg(short = 'p', long)]
218
        program_id: Option<String>,
219

            
220
        #[arg(short = 'i')]
221
        input_file: Option<String>,
222

            
223
        #[arg(short = 'e', long)]
224
        execution_id: String,
225

            
226
        #[arg(short = 'o')]
227
        output_location: Option<String>,
228
    },
229

            
230
    #[command(about = "Initialize a new project")]
231
    Init {
232
        #[arg(short = 'd', long)]
233
        dir: Option<String>,
234

            
235
        #[arg(short = 'n', long)]
236
        project_name: String,
237
    },
238

            
239
    #[command(about = "Read and display the journal from a binary proof receipt file")]
240
    ReadReceipt {
241
        #[arg(help = "The path to the binary proof receipt file (.bin)")]
242
        receipt_path: String,
243
    },
244
}