use anyhow::Error;
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use fehler::throws;
mod command;
use command::FuzzCommand;
use termimad::MadSkin;
macro_rules! load_template {
($file:expr) => {
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), $file))
};
}
#[derive(Parser)]
#[command(
name = "Trident",
about = "Trident is Rust based fuzzer for Solana programs written using Anchor framework."
)]
struct Cli {
#[clap(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
#[command(about = "Show the HowTo message.")]
How,
#[command(
about = "Initialize Trident in the current Anchor workspace.",
override_usage = "\nTrident will skip initialization if Trident.toml already exists."
)]
Init {
#[arg(
short,
long,
required = false,
help = "Force Trident initialization. Trident dependencies will be updated based on the version of Trident CLI."
)]
force: bool,
},
#[command(
about = "Run fuzz subcommands.",
override_usage = "With fuzz subcommands you can add new fuzz test \
template or you can run fuzz test on already initialzied one.\
\n\n\x1b[1m\x1b[4mEXAMPLE:\x1b[0m\
\n trident add\
\n trident fuzz run-hfuzz fuzz_0\
\n trident fuzz debug-hfuzz \x1b[92m<FUZZ_TARGET>\x1b[0m \x1b[92m<PATH_TO_CRASHFILE>\x1b[0m"
)]
Fuzz {
#[clap(subcommand)]
subcmd: FuzzCommand,
},
#[command(about = "Clean Honggfuzz build targets ,additionally perform `anchor clean`")]
Clean,
}
#[throws]
pub async fn start() {
let cli = Cli::parse();
match cli.command {
Command::How => command::howto()?,
Command::Fuzz { subcmd } => command::fuzz(subcmd).await?,
Command::Init { force } => command::init(force).await?,
Command::Clean => command::clean().await?,
}
}
fn _discover(target: &str) -> Result<Option<String>> {
let _cwd = std::env::current_dir()?;
let mut cwd_opt = Some(_cwd.as_path());
while let Some(cwd) = cwd_opt {
for f in std::fs::read_dir(cwd)
.with_context(|| format!("Error reading the directory with path: {}", cwd.display()))?
{
let p = f
.with_context(|| {
format!("Error reading the directory with path: {}", cwd.display())
})?
.path();
if let Some(filename) = p.file_name() {
if filename.to_str() == Some(target) {
return Ok(Some(cwd.to_string_lossy().to_string()));
}
}
}
cwd_opt = cwd.parent();
}
Ok(None)
}
fn show_howto() {
let markdown_input = load_template!("/src/howto.md");
let skin = MadSkin::default();
skin.print_text(markdown_input);
}