use std::{fmt::Display, str::FromStr};
use clap::{Parser, Subcommand, ValueEnum};
use radicle::{node::notifications::NotificationId, prelude::RepoId};
const ABOUT: &str = "Manage your Radicle notifications";
const LONG_ABOUT: &str = r#"
By default, this command lists all items in your inbox.
If your working directory is a Radicle repository, it only shows items
belonging to this repository, unless `--all` is used.
The `show` subcommand takes a notification ID (which can be found in
the output of the `list` subcommand) and displays the information related to that
notification. This will mark the notification as read.
The `clear` subcommand will clear all notifications with given IDs,
or all notifications if no IDs are given. Cleared notifications are
deleted and cannot be restored.
"#;
#[derive(Clone, Debug, Parser)]
#[command(about = ABOUT, long_about = LONG_ABOUT, disable_version_flag = true)]
pub struct Args {
#[command(subcommand)]
pub(super) command: Option<Command>,
#[clap(flatten)]
pub(super) empty: EmptyArgs,
}
#[derive(Subcommand, Clone, Debug)]
pub(super) enum Command {
List(ListArgs),
Show {
#[arg(value_name = "NOTIFICATION_ID")]
id: NotificationId,
},
Clear(ClearArgs),
}
#[derive(Parser, Clone, Copy, Debug)]
pub(super) struct EmptyArgs {
#[arg(long, value_enum, default_value_t, hide = true)]
sort_by: SortBy,
#[arg(short, long, hide = true)]
reverse: bool,
#[arg(long, hide = true)]
show_unknown: bool,
#[arg(value_name = "RID")]
#[arg(long, hide = true)]
repo: Option<RepoId>,
#[arg(short, long, conflicts_with = "repo", hide = true)]
all: bool,
}
#[derive(Parser, Clone, Copy, Debug)]
pub(super) struct ListArgs {
#[arg(long, value_enum, default_value_t)]
pub(super) sort_by: SortBy,
#[arg(short, long)]
pub(super) reverse: bool,
#[arg(long)]
pub(super) show_unknown: bool,
#[arg(long, value_name = "RID")]
pub(super) repo: Option<RepoId>,
#[arg(short, long, conflicts_with = "repo")]
pub(super) all: bool,
}
impl From<ListArgs> for ListMode {
fn from(args: ListArgs) -> Self {
if args.all {
assert!(args.repo.is_none());
return Self::All;
}
if let Some(repo) = args.repo {
return Self::ByRepo(repo);
}
Self::Contextual
}
}
impl From<EmptyArgs> for ListArgs {
fn from(
EmptyArgs {
sort_by,
reverse,
show_unknown,
repo,
all,
}: EmptyArgs,
) -> Self {
Self {
sort_by,
reverse,
show_unknown,
repo,
all,
}
}
}
#[derive(Parser, Clone, Debug)]
pub(super) struct ClearArgs {
#[arg(long, value_name = "RID")]
repo: Option<RepoId>,
#[arg(short, long, conflicts_with = "repo")]
all: bool,
#[arg(value_name = "NOTIFICATION_ID")]
ids: Option<Vec<NotificationId>>,
}
impl From<ClearArgs> for ClearMode {
fn from(ClearArgs { repo, all, ids }: ClearArgs) -> Self {
if let Some(ids) = ids {
return Self::ByNotifications(ids);
}
if all {
assert!(repo.is_none());
return Self::All;
}
if let Some(repo) = repo {
return Self::ByRepo(repo);
}
Self::Contextual
}
}
#[derive(ValueEnum, Clone, Copy, Default, Debug)]
pub enum SortBy {
Id,
#[default]
Timestamp,
}
impl Display for SortBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Id => write!(f, "rowid"),
Self::Timestamp => write!(f, "timestamp"),
}
}
}
impl FromStr for SortBy {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"id" => Ok(Self::Id),
"timestamp" => Ok(Self::Timestamp),
_ => Err(format!("'{s}' is not a valid sort by column")),
}
}
}
pub(super) enum ListMode {
Contextual,
All,
ByRepo(RepoId),
}
pub(super) enum ClearMode {
ByNotifications(Vec<NotificationId>),
ByRepo(RepoId),
All,
Contextual,
}