use anyhow::{Result, anyhow};
use clap::{ArgAction, Parser, Subcommand};
use std::path::{Path, PathBuf};
use tokio::fs;
use tracing::{Level, error, instrument};
use types::flip::Flip;
use crate::art::{FLIPPY, get_art};
mod art;
mod commands;
mod flipper;
mod git;
mod progress;
mod types;
mod validators;
mod walking_diff;
#[derive(Parser, Debug)]
#[command(
version,
about = format!("{FLIPPY}{}", env!("CARGO_PKG_DESCRIPTION")),
subcommand_required = true
)]
struct Cli {
#[arg(short, long, action = ArgAction::Count)]
verbose: u8,
#[arg(short, long)]
json: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
New {
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
Upload {
#[arg(short, long)]
force_walkdir: bool,
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
Map {
#[arg(value_parser = ["subghz","rfid","nfc","ir","ibutton","badusb"])]
db_type: String,
repo: String,
pathspec: PathBuf,
#[arg(long)]
excludes: bool,
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
Repo {
#[command(subcommand)]
command: RepoCommand,
},
Firmware {
#[command(subcommand)]
command: FirmwareCommand,
},
Store {
#[command(subcommand)]
command: StoreCommand,
},
}
#[derive(Subcommand, Debug)]
enum RepoCommand {
Add {
#[arg(value_parser)]
url: url::Url,
name: String,
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
Remove {
name: String,
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
}
#[derive(Subcommand, Debug)]
enum FirmwareCommand {
Set {
firmware: String,
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
Update {
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
}
#[derive(Subcommand, Debug)]
enum StoreCommand {
Fetch {
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
Clean {
#[arg(value_parser, default_value = ".")]
path: PathBuf,
},
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
init_logging(cli.verbose, cli.json);
if let Err(err) = run(cli).await {
println!("{}", get_art());
error!("{:?}", err);
std::process::exit(1);
}
Ok(())
}
async fn run(cli: Cli) -> Result<()> {
match cli.command {
Commands::New { path } => {
commands::new::run(path).await?;
}
Commands::Upload {
force_walkdir,
path,
} => {
let flip = try_flip_from_path(&path).await?;
commands::upload::run(flip, force_walkdir).await?;
}
Commands::Map {
db_type,
repo,
pathspec,
excludes,
path,
} => {
let flip = try_flip_from_path(&path).await?;
commands::map::run(flip, db_type, repo, pathspec, excludes).await?;
}
Commands::Repo { command } => match command {
RepoCommand::Add { url, name, path } => {
let flip = try_flip_from_path(&path).await?;
commands::repo::add(flip, url, name).await?;
}
RepoCommand::Remove { name, path } => {
let flip = try_flip_from_path(&path).await?;
commands::repo::remove(flip, name).await?;
}
},
Commands::Firmware { command } => match command {
FirmwareCommand::Set { firmware, path } => {
let flip = try_flip_from_path(&path).await?;
commands::firmware::set(flip, firmware).await?;
}
FirmwareCommand::Update { path } => {
let flip = try_flip_from_path(&path).await?;
commands::firmware::update(flip).await?;
}
},
Commands::Store { command } => match command {
StoreCommand::Fetch { path } => {
let flip = try_flip_from_path(&path).await?;
commands::store::fetch(flip).await?;
}
StoreCommand::Clean { path } => {
let flip = try_flip_from_path(&path).await?;
commands::store::clean(flip).await?;
}
},
}
Ok(())
}
async fn try_flip_from_path(p: impl AsRef<Path>) -> Result<Flip> {
let project = fs::canonicalize(p).await?;
if !Flip::exists(&project).await? {
return Err(anyhow!("No flippy project found at {}", project.display()));
}
let flip = Flip::from_path(&project).await?;
Ok(flip)
}
#[instrument]
fn init_logging(level: u8, json: bool) {
let level = match level {
0 => Level::INFO,
1 => Level::DEBUG,
2 => Level::TRACE,
_ => Level::TRACE,
};
let trace = level == Level::TRACE;
if trace {
unsafe {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
}
}
if json {
tracing_subscriber::fmt().json().init();
} else {
tracing_subscriber::fmt()
.compact()
.with_max_level(level)
.with_target(true)
.with_thread_ids(false)
.with_thread_names(false)
.with_level(true)
.with_target(true)
.with_thread_ids(false)
.with_thread_names(false)
.without_time()
.with_target(true)
.with_file(trace)
.with_line_number(trace)
.init()
};
}