use anyhow::Result;
use clap::{CommandFactory, Parser, Subcommand};
use std::path::PathBuf;
mod commands;
mod display;
use display::Format;
#[derive(Parser)]
#[command(name = "paracas")]
#[command(about = "High-performance Dukascopy tick data downloader", long_about = None)]
#[command(version)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
verbose: u8,
#[arg(short, long, global = true)]
quiet: bool,
#[arg(long, hide = true)]
daemon_run: Option<String>,
}
#[derive(Subcommand)]
enum Commands {
Download {
instrument: String,
#[arg(short, long)]
start: Option<String>,
#[arg(short, long)]
end: Option<String>,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(short, long, value_enum, default_value = "csv")]
format: Format,
#[arg(short, long)]
timeframe: Option<String>,
#[arg(long, default_value = "32")]
concurrency: usize,
#[arg(long)]
background: bool,
#[arg(long)]
yes: bool,
},
List {
#[arg(short, long)]
category: Option<String>,
#[arg(short, long)]
search: Option<String>,
},
Info {
instrument: String,
},
Status {
job_id: Option<String>,
#[arg(long)]
running: bool,
#[arg(long)]
all: bool,
#[arg(short, long)]
follow: Option<u64>,
#[arg(long, num_args = 0..=1, default_missing_value = "")]
cancel: Option<String>,
},
DownloadAll {
#[arg(short, long)]
category: Option<String>,
#[arg(short, long)]
start: Option<String>,
#[arg(short, long)]
end: Option<String>,
#[arg(short, long, default_value = ".")]
output_dir: PathBuf,
#[arg(short, long, value_enum, default_value = "csv")]
format: Format,
#[arg(short, long)]
timeframe: Option<String>,
#[arg(long, default_value = "4")]
parallel_instruments: usize,
#[arg(long, default_value = "32")]
concurrency: usize,
#[arg(long)]
background: bool,
#[arg(long)]
yes: bool,
},
Job {
#[command(subcommand)]
action: JobAction,
},
}
#[derive(Subcommand)]
enum JobAction {
Pause {
job_id: Option<String>,
},
Resume {
job_id: Option<String>,
},
Kill {
job_id: Option<String>,
},
Clean {
#[arg(long)]
all: bool,
},
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
if let Some(job_id) = cli.daemon_run {
return commands::daemon_run::daemon_run(&job_id).await;
}
let Some(command) = cli.command else {
Cli::command().print_help()?;
return Ok(());
};
match command {
Commands::Download {
instrument,
start,
end,
output,
format,
timeframe,
concurrency,
background,
yes,
} => {
commands::download::download(
&instrument,
start.as_deref(),
end.as_deref(),
output,
format,
timeframe.as_deref(),
concurrency,
background,
yes,
cli.quiet,
)
.await
}
Commands::List { category, search } => {
commands::list::list_instruments(category.as_deref(), search.as_deref())
}
Commands::Info { instrument } => commands::info::show_info(&instrument),
Commands::Status {
job_id,
running,
all,
follow,
cancel,
} => commands::status::status(job_id.as_deref(), running, all, follow, cancel.as_deref()),
Commands::DownloadAll {
category,
start,
end,
output_dir,
format,
timeframe,
parallel_instruments,
concurrency,
background,
yes,
} => {
commands::download_all::download_all(
category.as_deref(),
start.as_deref(),
end.as_deref(),
output_dir,
format,
timeframe.as_deref(),
parallel_instruments,
concurrency,
background,
yes,
cli.quiet,
)
.await
}
Commands::Job { action } => match action {
JobAction::Pause { job_id } => {
commands::job::job_command("pause", job_id.as_deref(), false)
}
JobAction::Resume { job_id } => {
commands::job::job_command("resume", job_id.as_deref(), false)
}
JobAction::Kill { job_id } => {
commands::job::job_command("kill", job_id.as_deref(), false)
}
JobAction::Clean { all } => commands::job::job_command("clean", None, all),
},
}
}