use std::{path::PathBuf, str::FromStr};
use clap::{AppSettings, ArgEnum, Parser, ValueHint};
use clap_complete::Shell;
use color_eyre::eyre::{eyre, Result};
use serde_derive::{Deserialize, Serialize};
use slog::Level;
pub(crate) const FALLBACK_CONFIG_PATH: &str = "dotfiles/.config/up/up.yaml";
pub(crate) const LATEST_RELEASE_URL: &str =
"https://api.github.com/repos/gibfahn/up-rs/releases/latest";
#[cfg(target_os = "linux")]
pub(crate) const SELF_UPDATE_URL: &str =
"https://github.com/gibfahn/up-rs/releases/latest/download/up-linux";
#[cfg(target_os = "macos")]
pub(crate) const SELF_UPDATE_URL: &str =
"https://github.com/gibfahn/up-rs/releases/latest/download/up-darwin";
#[must_use]
pub fn parse() -> Opts {
Opts::parse()
}
#[allow(clippy::doc_markdown, rustdoc::bare_urls)]
#[derive(Debug, Parser)]
#[clap(version, setting(AppSettings::UseLongFormatForHelpSubcommand))]
pub struct Opts {
#[clap(long, short = 'l', default_value = "info", env = "LOG_LEVEL", parse(try_from_str = from_level))]
pub log_level: Level,
#[clap(long, value_hint = ValueHint::DirPath)]
pub up_dir: Option<PathBuf>,
#[clap(long, default_value = "debug", env = "FILE_LOG_LEVEL", parse(try_from_str = from_level))]
pub file_log_level: Level,
#[clap(long, default_value = "auto", ignore_case = true, arg_enum)]
pub color: Color,
#[clap(long, short = 'c', default_value = "$XDG_CONFIG_HOME/up/up.yaml", value_hint = ValueHint::FilePath)]
pub(crate) config: String,
#[clap(subcommand)]
pub(crate) cmd: Option<SubCommand>,
}
fn from_level(level: &str) -> Result<Level> {
Level::from_str(level).map_err(|()| eyre!("Failed to parse level {level}"))
}
#[derive(Debug, ArgEnum, Clone)]
pub enum Color {
Auto,
Always,
Never,
}
#[derive(Debug, Parser)]
pub(crate) enum SubCommand {
Run(RunOptions),
Link(LinkOptions),
Git(GitOptions),
Defaults(DefaultsOptions),
Generate(GenerateOptions),
Self_(UpdateSelfOptions),
Completions(CompletionsOptions),
List(RunOptions),
}
#[derive(Debug, Parser, Default)]
pub(crate) struct RunOptions {
#[clap(short, long)]
pub(crate) bootstrap: bool,
#[clap(short = 'f', long, value_hint = ValueHint::Url)]
pub(crate) fallback_url: Option<String>,
#[clap(short = 'p', long, default_value = FALLBACK_CONFIG_PATH, value_hint = ValueHint::FilePath)]
pub(crate) fallback_path: String,
#[clap(short, long, multiple_values(false), multiple_occurrences(true))]
pub(crate) tasks: Option<Vec<String>>,
}
#[derive(Debug, Parser, Default, Serialize, Deserialize)]
pub(crate) struct LinkOptions {
#[clap(short = 'f', long = "from", default_value = "~/code/dotfiles", value_hint = ValueHint::DirPath)]
pub(crate) from_dir: String,
#[clap(short = 't', long = "to", default_value = "~", value_hint = ValueHint::DirPath)]
pub(crate) to_dir: String,
}
#[derive(Debug, Default, Parser)]
pub struct GitOptions {
#[clap(long, value_hint = ValueHint::Url)]
pub git_url: String,
#[clap(long, value_hint = ValueHint::DirPath)]
pub git_path: String,
#[clap(long, default_value = crate::tasks::git::DEFAULT_REMOTE_NAME)]
pub remote: String,
#[clap(long)]
pub branch: Option<String>,
#[clap(long)]
pub prune: bool,
}
#[derive(Debug, Parser)]
pub(crate) struct GenerateOptions {
#[clap(subcommand)]
pub(crate) lib: Option<GenerateLib>,
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub(crate) struct UpdateSelfOptions {
#[clap(long, default_value = SELF_UPDATE_URL, value_hint = ValueHint::Url)]
pub(crate) url: String,
#[clap(long)]
pub(crate) always_update: bool,
}
#[derive(Debug, Parser)]
pub(crate) struct CompletionsOptions {
#[clap(arg_enum)]
pub(crate) shell: Shell,
}
impl Default for UpdateSelfOptions {
fn default() -> Self {
Self {
url: SELF_UPDATE_URL.to_owned(),
always_update: false,
}
}
}
#[derive(Debug, Parser)]
pub(crate) enum GenerateLib {
Git(GenerateGitConfig),
Defaults(GenerateDefaultsConfig),
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub struct GenerateGitConfig {
#[clap(long, parse(from_str), value_hint = ValueHint::FilePath)]
pub(crate) path: PathBuf,
#[clap(long, parse(from_str), default_value = "~", value_hint = ValueHint::DirPath)]
pub(crate) search_paths: Vec<PathBuf>,
#[clap(long)]
pub(crate) excludes: Option<Vec<String>>,
#[clap(long)]
pub(crate) prune: bool,
#[clap(long)]
pub(crate) remote_order: Vec<String>,
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub struct GenerateDefaultsConfig {
#[clap(long, parse(from_str), value_hint = ValueHint::FilePath)]
pub(crate) path: PathBuf,
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub struct DefaultsOptions {
#[clap(subcommand)]
pub(crate) subcommand: DefaultsSubcommand,
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub enum DefaultsSubcommand {
Read(DefaultsReadOptions),
Write(DefaultsWriteOptions),
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub struct DefaultsReadOptions {
#[clap(short = 'g', long = "globalDomain")]
pub(crate) global_domain: bool,
pub(crate) domain: Option<String>,
pub(crate) key: Option<String>,
}
#[derive(Debug, Parser, Serialize, Deserialize)]
pub struct DefaultsWriteOptions {
#[clap(short = 'g', long = "globalDomain")]
pub(crate) global_domain: bool,
pub(crate) domain: String,
pub(crate) key: String,
pub(crate) value: Option<String>,
}