Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add command: rad issue search
Matthias Beyer committed 1 month ago
commit 2d60db84a52ac3f517d3a55d6ab06347fb8a40cb
parent 7bac17146c40b32b2ce0e27b96a555a44f3ef2ba
2 files changed +127 -0
modified crates/radicle-cli/src/commands/issue.rs
@@ -112,6 +112,101 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
                args.quiet,
            )?,
        },
+
        Command::Search {
+
            substring,
+
            title,
+
            no_title,
+
            comments,
+
            no_comments,
+
            author,
+
            state,
+
        } => {
+
            // Search title by default or when --title is passed.
+
            // Do not search title when --no-title is passed
+
            let do_search_title = !title || no_title;
+

+
            // Search comments by default or when --comments is passed.
+
            // Do not search comments when --no-comments is passed
+
            let do_search_comments = !comments || no_comments;
+

+
            let filter_state = Option::<State>::from(&state);
+

+
            let filter = |issue: &issue::Issue| {
+
                if let Some(s) = filter_state.as_ref() {
+
                    if issue.state() != s {
+
                        return false;
+
                    }
+
                }
+

+
                if let Some(did) = author {
+
                    if issue.author().id != did {
+
                        return false;
+
                    }
+
                }
+

+
                let mut matches_search = issue.description().contains(&substring);
+
                matches_search |= do_search_title && issue.title().contains(&substring);
+
                matches_search |= do_search_comments
+
                    && issue
+
                        .comments()
+
                        .any(|(_, comment)| comment.body().contains(&substring));
+

+
                matches_search
+
            };
+

+
            let filter_issue_result = |r: &Result<_, _>| {
+
                let Ok((_oid, issue)) = r else { return true };
+

+
                filter(issue)
+
            };
+

+
            let mut found_issues = issues
+
                .list()?
+
                .filter(filter_issue_result)
+
                .collect::<Result<Vec<_>, _>>()?;
+

+
            found_issues.sort_by(|(id1, i1), (id2, i2)| {
+
                let by_timestamp = i2.timestamp().cmp(&i1.timestamp());
+
                let by_id = id1.cmp(id2);
+

+
                by_timestamp.then(by_id)
+
            });
+

+
            let mut table = term::Table::new(term::table::TableOptions::bordered());
+
            table.header([
+
                term::format::dim(String::from("●")).into(),
+
                term::format::bold(String::from("ID")).into(),
+
                term::format::bold(String::from("Title")).into(),
+
                term::format::bold(String::from("Author")).into(),
+
                term::Line::blank(),
+
                term::format::bold(String::from("Labels")).into(),
+
                term::format::bold(String::from("Assignees")).into(),
+
                term::format::bold(String::from("Opened")).into(),
+
            ]);
+
            table.divider();
+

+
            table.extend(found_issues.into_iter().map(|(id, issue)| {
+
                let assigned: String = issue
+
                    .assignees()
+
                    .map(|did| {
+
                        let (alias, _) = Author::new(did.as_key(), &profile, args.verbose).labels();
+

+
                        alias.content().to_owned()
+
                    })
+
                    .collect::<Vec<_>>()
+
                    .join(", ");
+

+
                let mut labels = issue.labels().map(|t| t.to_string()).collect::<Vec<_>>();
+
                labels.sort();
+

+
                let author = issue.author().id;
+
                let (alias, did) = Author::new(&author, &profile, args.verbose).labels();
+

+
                mk_issue_row(id, issue, assigned, labels, alias, did)
+
            }));
+

+
            table.print();
+
        }
        Command::Show { id } => {
            let format = if args.header {
                term::issue::Format::Header
modified crates/radicle-cli/src/commands/issue/args.rs
@@ -164,6 +164,37 @@ pub(crate) enum Command {
        #[arg(value_name = "COMMENT_ID")]
        comment_id: Option<Rev>,
    },
+
    /// Search for issues
+
    Search {
+
        /// The substring to search for
+
        substring: String,
+

+
        /// Whether to grep in the title as well
+
        #[clap(long, action = clap::ArgAction::SetTrue, overrides_with = "no_title")]
+
        #[clap(default_value_t = true)]
+
        title: bool,
+

+
        #[clap(long = "no-title", action = clap::ArgAction::SetFalse, overrides_with = "title")]
+
        #[clap(hide = true)]
+
        no_title: bool,
+

+
        /// Whether to grep in the title as well
+
        #[clap(long, action = clap::ArgAction::SetTrue, overrides_with = "no_comments")]
+
        #[clap(default_value_t = true)]
+
        comments: bool,
+

+
        #[clap(long = "no-comments", action = clap::ArgAction::SetFalse, overrides_with = "comments")]
+
        #[clap(hide = true)]
+
        no_comments: bool,
+

+
        /// Filter by issue author
+
        #[clap(long = "author")]
+
        #[clap(value_name = "DID")]
+
        author: Option<Did>,
+

+
        #[clap(flatten)]
+
        state: ListStateArgs,
+
    },
    /// Show a specific issue
    Show {
        /// ID of the issue
@@ -189,6 +220,7 @@ impl Command {
        match self {
            Command::Open { .. }
            | Command::React { .. }
+
            | Command::Search { .. }
            | Command::State { .. }
            | Command::Delete { .. }
            | Command::Assign { .. }