use clap::{Parser, Subcommand};
use sails_cli::{
idlgen::CrateIdlGenerator, program::ProgramGenerator, program_new, solgen::SolidityGenerator,
};
use sails_client_gen::ClientGenerator;
use std::{error::Error, path::PathBuf};
#[derive(Parser)]
#[command(bin_name = "cargo")]
enum CliCommand {
#[command(name = "sails", subcommand)]
Sails(SailsCommands),
}
#[derive(Subcommand)]
enum SailsCommands {
#[command(name = "program")]
NewProgram {
#[arg(help = "Path to the new program")]
path: String,
#[arg(short, long, help = "Name of the new program")]
name: Option<String>,
#[arg(
long,
help = "Disable generation of client package alongside the program. Implies '--no-gtest'"
)]
no_client: bool,
#[arg(long, help = "Disable generation of program tests using 'gtest'")]
no_gtest: bool,
#[arg(long, help = "Use 'sails-rs' crate of the specified version")]
sails_version: Option<String>,
},
#[command(name = "new")]
New {
#[arg(value_hint = clap::ValueHint::DirPath)]
path: PathBuf,
#[arg(long)]
name: Option<String>,
#[arg(long)]
author: Option<String>,
#[arg(long)]
username: Option<String>,
#[arg(long, value_hint = clap::ValueHint::DirPath)]
sails_path: Option<PathBuf>,
#[arg(long)]
offline: bool,
#[arg(long)]
eth: bool,
},
#[command(name = "client-rs")]
ClientRs {
#[arg(value_hint = clap::ValueHint::FilePath)]
idl_path: PathBuf,
#[arg(value_hint = clap::ValueHint::FilePath)]
out_path: Option<PathBuf>,
#[arg(long)]
mocks: Option<String>,
#[arg(long)]
sails_crate: Option<String>,
#[arg(long, short = 'T', value_parser = parse_key_val::<String, String>)]
external_types: Vec<(String, String)>,
#[arg(long)]
no_derive_traits: bool,
},
#[command(name = "idl")]
IdlGen {
#[arg(long, value_hint = clap::ValueHint::FilePath)]
manifest_path: Option<PathBuf>,
#[arg(long, value_hint = clap::ValueHint::DirPath)]
target_dir: Option<PathBuf>,
#[arg(long)]
deps_level: Option<usize>,
},
#[command(name = "sol")]
SolGen {
#[arg(long, value_hint = clap::ValueHint::FilePath)]
idl_path: PathBuf,
#[arg(long, value_hint = clap::ValueHint::DirPath)]
target_dir: Option<PathBuf>,
#[arg(long, short = 'n')]
contract_name: Option<String>,
},
}
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
where
T: std::str::FromStr,
T::Err: Error + Send + Sync + 'static,
U: std::str::FromStr,
U::Err: Error + Send + Sync + 'static,
{
let pos = s
.find('=')
.ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?;
Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
}
fn main() -> Result<(), i32> {
let CliCommand::Sails(command) = CliCommand::parse();
let result = match command {
SailsCommands::NewProgram {
path,
name,
no_client,
no_gtest,
sails_version,
} => {
let program_generator = ProgramGenerator::new(path)
.with_name(name)
.with_client(!no_client)
.with_gtest(!no_gtest)
.with_sails_version(sails_version);
program_generator.generate()
}
SailsCommands::New {
path,
name,
author,
username,
sails_path,
offline,
eth,
} => program_new::ProgramGenerator::new(
path, name, author, username, sails_path, offline, eth,
)
.generate(),
SailsCommands::ClientRs {
idl_path,
out_path,
mocks,
sails_crate,
external_types,
no_derive_traits,
} => {
let mut client_gen = ClientGenerator::from_idl_path(idl_path.as_ref());
if let Some(mocks) = mocks.as_ref() {
client_gen = client_gen.with_mocks(mocks);
}
if let Some(sails_crate) = sails_crate.as_ref() {
client_gen = client_gen.with_sails_crate(sails_crate);
}
for (name, path) in external_types.iter() {
client_gen = client_gen.with_external_type(name, path);
}
if no_derive_traits {
client_gen = client_gen.with_no_derive_traits();
}
let out_path = out_path.unwrap_or_else(|| idl_path.with_extension("rs"));
client_gen.generate_to(out_path)
}
SailsCommands::IdlGen {
manifest_path,
target_dir,
deps_level,
} => CrateIdlGenerator::new(manifest_path, target_dir, deps_level).generate(),
SailsCommands::SolGen {
idl_path,
target_dir,
contract_name,
} => SolidityGenerator::new(idl_path, target_dir, contract_name).generate(),
};
if let Err(e) = result {
eprintln!("Error: {e:#}");
return Err(-1);
}
Ok(())
}