use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "fastpaper", version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[command(flatten)]
pub global: GlobalOpts,
}
#[derive(clap::Args)]
pub struct GlobalOpts {
#[arg(short, long, global = true, action = clap::ArgAction::Count)]
pub verbose: u8,
#[arg(short, long, global = true)]
pub quiet: bool,
#[arg(short, long, global = true, default_value = "table")]
pub format: OutputFormat,
}
#[derive(Subcommand)]
pub enum Commands {
Search(SearchArgs),
Download(DownloadArgs),
Read(ReadArgs),
Get(GetArgs),
Sources(SourcesArgs),
Completions {
shell: clap_complete::Shell,
},
}
#[derive(clap::Args)]
pub struct SearchArgs {
pub source: Source,
pub query: String,
#[arg(short = 'n', long, default_value = "10")]
pub limit: u32,
#[arg(long, default_value = "0")]
pub offset: u32,
#[arg(long, default_value = "relevance")]
pub sort: SortField,
#[arg(long, default_value = "desc")]
pub order: SortOrder,
#[arg(long)]
pub author: Option<String>,
#[arg(long)]
pub after: Option<String>,
#[arg(long)]
pub before: Option<String>,
#[arg(long)]
pub year: Option<u16>,
#[arg(long)]
pub field: Option<String>,
#[arg(long)]
pub open_access: bool,
#[arg(long)]
pub peer_reviewed: bool,
#[arg(long)]
pub fields: Option<String>,
#[arg(long)]
pub with_abstract: bool,
#[arg(short, long)]
pub output: Option<PathBuf>,
}
#[derive(clap::Args)]
pub struct DownloadArgs {
pub source: Source,
pub identifier: String,
#[arg(short, long, env = "FASTPAPER_DOWNLOAD_DIR", default_value = "./papers")]
pub dir: PathBuf,
#[arg(long, default_value = "{id}.{title}")]
pub filename: String,
#[arg(long)]
pub overwrite: bool,
#[arg(long)]
pub source_files: bool,
}
#[derive(clap::Args)]
pub struct ReadArgs {
pub source: Source,
pub identifier: String,
#[arg(long, default_value = "full")]
pub section: Section,
#[arg(long)]
pub metadata_only: bool,
#[arg(long)]
pub raw: bool,
#[arg(long)]
pub max_length: Option<usize>,
#[arg(short, long)]
pub output: Option<PathBuf>,
}
#[derive(clap::Args)]
pub struct GetArgs {
pub identifier: String,
#[arg(long)]
pub resolve: bool,
#[arg(long)]
pub with_citations: bool,
#[arg(long)]
pub with_abstract: bool,
#[arg(long)]
pub with_related: bool,
}
#[derive(clap::Args)]
pub struct SourcesArgs {
#[arg(long)]
pub check: bool,
#[arg(long)]
pub capabilities: bool,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum Source {
Arxiv,
Biorxiv,
Medrxiv,
Pubmed,
Pmc,
Europepmc,
Scholar,
Xueshu,
Semantic,
Crossref,
Openalex,
Dblp,
Core,
Openaire,
Doaj,
Unpaywall,
Zenodo,
Hal,
Local,
}
impl Source {
pub fn supports_search(&self) -> bool {
!matches!(self, Source::Local)
}
pub fn supports_download(&self) -> bool {
matches!(
self,
Source::Arxiv
| Source::Biorxiv
| Source::Medrxiv
| Source::Pmc
| Source::Semantic
| Source::Core
| Source::Doaj
| Source::Zenodo
| Source::Hal
| Source::Local
)
}
pub fn supports_read(&self) -> bool {
self.supports_download()
}
pub fn name(&self) -> &'static str {
match self {
Source::Arxiv => "arxiv",
Source::Biorxiv => "biorxiv",
Source::Medrxiv => "medrxiv",
Source::Pubmed => "pubmed",
Source::Pmc => "pmc",
Source::Europepmc => "europepmc",
Source::Scholar => "scholar",
Source::Xueshu => "xueshu",
Source::Semantic => "semantic",
Source::Crossref => "crossref",
Source::Openalex => "openalex",
Source::Dblp => "dblp",
Source::Core => "core",
Source::Openaire => "openaire",
Source::Doaj => "doaj",
Source::Unpaywall => "unpaywall",
Source::Zenodo => "zenodo",
Source::Hal => "hal",
Source::Local => "local",
}
}
pub fn download_hint(&self) -> Option<&'static str> {
match self {
Source::Pubmed => Some("Try: fastpaper download pmc <PMC_ID>"),
Source::Scholar => Some("Google Scholar does not provide PDFs directly"),
Source::Xueshu => {
Some("Baidu Xueshu aggregates external links only. Try: fastpaper get <DOI> --resolve")
}
Source::Crossref | Source::Openalex | Source::Dblp => {
Some("This source only provides metadata. Try: fastpaper get <ID> --resolve")
}
Source::Openaire | Source::Unpaywall => {
Some("This source only provides metadata. Try: fastpaper get <ID> --resolve")
}
_ => None,
}
}
}
#[derive(ValueEnum, Clone, Debug)]
pub enum OutputFormat {
Table,
Json,
Jsonl,
Csv,
Bibtex,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum SortField {
Relevance,
Date,
Citations,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum SortOrder {
Asc,
Desc,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum Section {
Abstract,
Introduction,
Methods,
Results,
Discussion,
Conclusion,
References,
Full,
}