Commands
Add in a main.rs
file that will be used to run the counter-app CLI:
cd counter-app
touch src/main.rs
Now in the main.rs
file add the use
statements:
#![allow(unused)] fn main() { use clap::{Args, Parser, Subcommand}; use counter_app::App; use essential_app_utils::compile::compile_pint_project; use essential_types::PredicateAddress; use std::path::PathBuf; }
Using the cli crate clap
add two commands:
#![allow(unused)] fn main() { #[derive(Parser)] #[command(version, about, long_about = None)] struct Cli { #[command(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { ReadCount { #[command(flatten)] server: Shared, }, IncrementCount { #[command(flatten)] server: Shared, }, } #[derive(Args)] pub struct Shared { /// The address of the server to connect to. pub server: String, /// The directory containing the pint files. pub pint_directory: PathBuf, } }
The command ReadCount
with become read-count <SERVER> <PINT_DIRECTORY>
which will read the current count from the server.
The command IncrementCount
with become increment-count <SERVER> <PINT_DIRECTORY>
which will increment the current count on the server.
Both commands take the same arguments so we use a Shared
struct to hold the arguments.
Add the main function to run the CLI:
#[tokio::main] async fn main() { let args = Cli::parse(); if let Err(err) = run(args).await { eprintln!("Command failed because: {}", err); } }
This is fairly simple and just handles errors in a nice way for the user.
For both commands we need to compile the pint project to get the address of the predicate and create a new App
.
Add this helper function:
#![allow(unused)] fn main() { async fn create_app(pint_directory: PathBuf, server: String) -> Result<App, anyhow::Error> { let counter = compile_pint_project(pint_directory).await?; let contract_address = essential_hash::contract_addr::from_contract(&counter); let predicate_address = essential_hash::content_addr(&counter.predicates[0]); let predicate_address = PredicateAddress { contract: contract_address, predicate: predicate_address, }; let app = App::new(server, predicate_address)?; Ok(app) } }
The core of the cli is the run function.
It should handle each command and use the App
to complete the actions like:
#![allow(unused)] fn main() { async fn run(cli: Cli) -> anyhow::Result<()> { let Cli { command } = cli; match command { Command::ReadCount { server: Shared { server, pint_directory, }, } => { let app = create_app(pint_directory, server).await?; let count = app.read_count().await?; println!("Current count is: {}", count); } Command::IncrementCount { server: Shared { server, pint_directory, }, } => { let app = create_app(pint_directory, server).await?; let new_count = app.increment().await?; println!("Incremented count to: {}", new_count); } } Ok(()) } }
Check your `main.rs` matches this.
use clap::{Args, Parser, Subcommand}; use counter_app::App; use essential_app_utils::compile::compile_pint_project; use essential_types::PredicateAddress; use std::path::PathBuf; #[derive(Parser)] #[command(version, about, long_about = None)] struct Cli { #[command(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { ReadCount { #[command(flatten)] server: Shared, }, IncrementCount { #[command(flatten)] server: Shared, }, } #[derive(Args)] pub struct Shared { /// The address of the server to connect to. pub server: String, /// The directory containing the pint files. pub pint_directory: PathBuf, } #[tokio::main] async fn main() { let args = Cli::parse(); if let Err(err) = run(args).await { eprintln!("Command failed because: {}", err); } } async fn run(cli: Cli) -> anyhow::Result<()> { let Cli { command } = cli; match command { Command::ReadCount { server: Shared { server, pint_directory, }, } => { let app = create_app(pint_directory, server).await?; let count = app.read_count().await?; println!("Current count is: {}", count); } Command::IncrementCount { server: Shared { server, pint_directory, }, } => { let app = create_app(pint_directory, server).await?; let new_count = app.increment().await?; println!("Incremented count to: {}", new_count); } } Ok(()) } async fn create_app(pint_directory: PathBuf, server: String) -> Result<App, anyhow::Error> { let counter = compile_pint_project(pint_directory).await?; let contract_address = essential_hash::contract_addr::from_contract(&counter); let predicate_address = essential_hash::content_addr(&counter.predicates[0]); let predicate_address = PredicateAddress { contract: contract_address, predicate: predicate_address, }; let app = App::new(server, predicate_address)?; Ok(app) }