Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add --filter-state param to `rad inbox` command
✗ CI failure Matthias Beyer committed 7 months ago
commit 50f54c88c46753e4eb871e4757dce75dfb026b1f
parent d686ca86e3e040aab8e1de063ccec81a1b4bdbec
3 failed (3 total) View logs
1 file changed +142 -21
modified crates/radicle-cli/src/commands/inbox.rs
@@ -47,12 +47,13 @@ Usage

Options

-
    --all                Operate on all repositories
-
    --repo <rid>         Operate on the given repository (default: rad .)
-
    --sort-by <field>    Sort by `id` or `timestamp` (default: timestamp)
-
    --reverse, -r        Reverse the list
-
    --show-unknown       Show any updates that were not recognized
-
    --help               Print help
+
    --all                  Operate on all repositories
+
    --repo <rid>           Operate on the given repository (default: rad .)
+
    --sort-by <field>      Sort by `id` or `timestamp` (default: timestamp)
+
    --filter-state <state> Filter by notification state (one of: merged, deleted, created, opened, updated, draft)
+
    --reverse, -r          Reverse the list
+
    --show-unknown         Show any updates that were not recognized
+
    --help                 Print help
"#,
};

@@ -83,9 +84,65 @@ pub struct Options {
    op: Operation,
    mode: Mode,
    sort_by: SortBy,
+
    filter_by: Option<FilterBy>,
    show_unknown: bool,
}

+
#[derive(Debug, Copy, Clone)]
+
struct FilterBy {
+
    state: NotificationState,
+
}
+

+
impl From<FilterBy> for radicle::node::notifications::store::FilterBy {
+
    fn from(value: FilterBy) -> Self {
+
        Self {
+
            ref_update: value.state.into(),
+
        }
+
    }
+
}
+

+
#[derive(Debug, Copy, Clone)]
+
enum NotificationState {
+
    Merged,
+
    Deleted,
+
    Created,
+
    Opened,
+
    Updated,
+
    Draft,
+
}
+

+
impl From<NotificationState> for store::FilterByRefUpdate {
+
    fn from(value: NotificationState) -> Self {
+
        match value {
+
            NotificationState::Merged => Self::Merged,
+
            NotificationState::Deleted => Self::Deleted,
+
            NotificationState::Created => Self::Created,
+
            NotificationState::Opened => Self::Opened,
+
            NotificationState::Updated => Self::Updated,
+
            NotificationState::Draft => Self::Draft,
+
        }
+
    }
+
}
+

+
impl NotificationState {
+
    fn matches(&self, ref_update: &radicle::storage::RefUpdate) -> bool {
+
        // TODO: How to match for the other "NotificationState"s?
+
        matches!(
+
            (self, ref_update),
+
            (
+
                NotificationState::Deleted,
+
                radicle::storage::RefUpdate::Deleted { .. }
+
            ) | (
+
                NotificationState::Created,
+
                radicle::storage::RefUpdate::Created { .. }
+
            ) | (
+
                NotificationState::Updated,
+
                radicle::storage::RefUpdate::Updated { .. }
+
            )
+
        )
+
    }
+
}
+

impl Args for Options {
    fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
        use lexopt::prelude::*;
@@ -96,6 +153,7 @@ impl Args for Options {
        let mut ids = Vec::new();
        let mut reverse = None;
        let mut field = None;
+
        let mut filter_state = None;
        let mut show_unknown = false;

        while let Some(arg) = parser.next()? {
@@ -125,6 +183,23 @@ impl Args for Options {
                        }
                    }
                }
+
                Long("filter-state") => {
+
                    let val = parser.value()?;
+

+
                    match term::args::string(&val).as_str() {
+
                        "merged" => filter_state = Some(NotificationState::Merged),
+
                        "deleted" => filter_state = Some(NotificationState::Deleted),
+
                        "created" => filter_state = Some(NotificationState::Created),
+
                        "opened" => filter_state = Some(NotificationState::Opened),
+
                        "updated" => filter_state = Some(NotificationState::Updated),
+
                        "draft" => filter_state = Some(NotificationState::Draft),
+
                        other => {
+
                            anyhow::bail!(
+
                                "unknown filtering filter_state `{other}`, see `rad inbox --help`"
+
                            )
+
                        }
+
                    }
+
                }
                Long("repo") if mode.is_none() => {
                    let val = parser.value()?;
                    let repo = args::rid(&val)?;
@@ -168,6 +243,7 @@ impl Args for Options {
                op,
                mode,
                sort_by,
+
                filter_by: filter_state.map(|state| FilterBy { state }),
                show_unknown,
            },
            vec![],
@@ -183,6 +259,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
        op,
        mode,
        sort_by,
+
        filter_by,
        show_unknown,
    } = options;

@@ -190,12 +267,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
        Operation::List => list(
            mode,
            sort_by,
+
            filter_by,
            show_unknown,
            &notifs.read_only(),
            storage,
            &profile,
        ),
-
        Operation::Clear => clear(mode, &mut notifs),
+
        Operation::Clear => clear(mode, filter_by, &mut notifs),
        Operation::Show => show(mode, &mut notifs, storage, &profile),
    }
}
@@ -203,6 +281,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
fn list(
    mode: Mode,
    sort_by: SortBy,
+
    filter_by: Option<FilterBy>,
    show_unknown: bool,
    notifs: &notifications::StoreReader,
    storage: &Storage,
@@ -211,17 +290,33 @@ fn list(
    let repos: Vec<term::VStack<'_>> = match mode {
        Mode::Contextual => {
            if let Ok((_, rid)) = radicle::rad::cwd() {
-
                list_repo(rid, sort_by, show_unknown, notifs, storage, profile)?
-
                    .into_iter()
-
                    .collect()
+
                list_repo(
+
                    rid,
+
                    sort_by,
+
                    filter_by,
+
                    show_unknown,
+
                    notifs,
+
                    storage,
+
                    profile,
+
                )?
+
                .into_iter()
+
                .collect()
            } else {
-
                list_all(sort_by, show_unknown, notifs, storage, profile)?
+
                list_all(sort_by, filter_by, show_unknown, notifs, storage, profile)?
            }
        }
-
        Mode::ByRepo(rid) => list_repo(rid, sort_by, show_unknown, notifs, storage, profile)?
-
            .into_iter()
-
            .collect(),
-
        Mode::All => list_all(sort_by, show_unknown, notifs, storage, profile)?,
+
        Mode::ByRepo(rid) => list_repo(
+
            rid,
+
            sort_by,
+
            filter_by,
+
            show_unknown,
+
            notifs,
+
            storage,
+
            profile,
+
        )?
+
        .into_iter()
+
        .collect(),
+
        Mode::All => list_all(sort_by, filter_by, show_unknown, notifs, storage, profile)?,
        Mode::ById(_) => anyhow::bail!("the `list` command does not take IDs"),
    };

@@ -237,6 +332,7 @@ fn list(

fn list_all<'a>(
    sort_by: SortBy,
+
    filter_by: Option<FilterBy>,
    show_unknown: bool,
    notifs: &notifications::StoreReader,
    storage: &Storage,
@@ -247,7 +343,15 @@ fn list_all<'a>(

    let mut vstacks = Vec::new();
    for repo in repos {
-
        let vstack = list_repo(repo.rid, sort_by, show_unknown, notifs, storage, profile)?;
+
        let vstack = list_repo(
+
            repo.rid,
+
            sort_by,
+
            filter_by,
+
            show_unknown,
+
            notifs,
+
            storage,
+
            profile,
+
        )?;
        vstacks.extend(vstack.into_iter());
    }
    Ok(vstacks)
@@ -256,6 +360,7 @@ fn list_all<'a>(
fn list_repo<'a, R: ReadStorage>(
    rid: RepoId,
    sort_by: SortBy,
+
    filter_by: Option<FilterBy>,
    show_unknown: bool,
    notifs: &notifications::StoreReader,
    storage: &R,
@@ -283,6 +388,12 @@ where
            Ok(n) => n,
        };

+
        if let Some(filter_by) = filter_by.as_ref() {
+
            if !filter_by.state.matches(&n.update) {
+
                continue;
+
            }
+
        }
+

        let seen = if n.status.is_read() {
            term::Label::blank()
        } else {
@@ -502,14 +613,24 @@ impl NotificationRow {
    }
}

-
fn clear(mode: Mode, notifs: &mut notifications::StoreWriter) -> anyhow::Result<()> {
+
fn clear(
+
    mode: Mode,
+
    filter_by: Option<FilterBy>,
+
    notifs: &mut notifications::StoreWriter,
+
) -> anyhow::Result<()> {
    let cleared = match mode {
-
        Mode::All => notifs.clear_all()?,
-
        Mode::ById(ids) => notifs.clear(&ids)?,
-
        Mode::ByRepo(rid) => notifs.clear_by_repo(&rid)?,
+
        Mode::All => {
+
            if filter_by.is_some() {
+
                // TODO: Is this needed?
+
                anyhow::bail!("Cannot filter when clearing all notifications. Doing nothing");
+
            }
+
            notifs.clear_all()?
+
        }
+
        Mode::ById(ids) => notifs.clear(filter_by.map(Into::into), &ids)?,
+
        Mode::ByRepo(rid) => notifs.clear_by_repo(filter_by.map(Into::into), &rid)?,
        Mode::Contextual => {
            if let Ok((_, rid)) = radicle::rad::cwd() {
-
                notifs.clear_by_repo(&rid)?
+
                notifs.clear_by_repo(filter_by.map(Into::into), &rid)?
            } else {
                return Err(Error::WithHint {
                    err: anyhow!("not a radicle repository"),