mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-10-18 19:10:36 +08:00
lib: Add text-based patch item filter
This commit is contained in:
Generated
+37
@@ -695,6 +695,15 @@ version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "fuzzy-matcher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
|
||||
dependencies = [
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -1087,6 +1096,12 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
@@ -1127,6 +1142,16 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.5.0"
|
||||
@@ -1615,10 +1640,12 @@ name = "radicle-tui"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fuzzy-matcher",
|
||||
"inquire",
|
||||
"lexopt",
|
||||
"libc",
|
||||
"log",
|
||||
"nom",
|
||||
"radicle",
|
||||
"radicle-surf",
|
||||
"radicle-term",
|
||||
@@ -2282,6 +2309,16 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
|
||||
@@ -19,8 +19,10 @@ flux = ["dep:tokio", "dep:tokio-stream"]
|
||||
anyhow = { version = "1" }
|
||||
inquire = { version = "0.6.2", default-features = false, features = ["termion", "editor"] }
|
||||
lexopt = { version = "0.3.0" }
|
||||
fuzzy-matcher = "*"
|
||||
libc = { version = "^0.2" }
|
||||
log = { version = "0.4.19" }
|
||||
nom = { version = "^7.1.0" }
|
||||
radicle = { git = "https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5" }
|
||||
radicle-term = { git = "https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5", package = "radicle-term" }
|
||||
radicle-surf = { version = "0.18.0" }
|
||||
|
||||
+143
-7
@@ -1,18 +1,26 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use nom::bytes::complete::{tag, take};
|
||||
use nom::multi::separated_list0;
|
||||
use nom::sequence::{delimited, preceded};
|
||||
use nom::{IResult, Parser};
|
||||
|
||||
use radicle::cob::{Label, ObjectId, Timestamp, TypedId};
|
||||
use radicle::crypto::PublicKey;
|
||||
use radicle::git::Oid;
|
||||
use radicle::identity::{Did, Identity};
|
||||
use radicle::node::{Alias, NodeId};
|
||||
use radicle::{issue, patch, Profile};
|
||||
use ratatui::style::{Color, Style, Stylize};
|
||||
use ratatui::widgets::Cell;
|
||||
|
||||
use radicle::cob::{Label, ObjectId, Timestamp, TypedId};
|
||||
use radicle::issue;
|
||||
use radicle::issue::{Issue, IssueId, Issues};
|
||||
use radicle::node::notifications::{Notification, NotificationId, NotificationKind};
|
||||
use radicle::node::AliasStore;
|
||||
use radicle::node::{Alias, AliasStore, NodeId};
|
||||
use radicle::patch;
|
||||
use radicle::patch::{Patch, PatchId, Patches};
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::storage::{ReadRepository, ReadStorage, RefUpdate, WriteRepository};
|
||||
use radicle::Profile;
|
||||
|
||||
use ratatui::style::{Color, Style, Stylize};
|
||||
use ratatui::widgets::Cell;
|
||||
|
||||
use super::super::git;
|
||||
use super::theme::style;
|
||||
@@ -490,6 +498,107 @@ impl ToRow<9> for PatchItem {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, Eq, PartialEq)]
|
||||
pub struct PatchItemFilter {
|
||||
status: Option<patch::Status>,
|
||||
authored: bool,
|
||||
authors: Vec<Did>,
|
||||
search: Option<String>,
|
||||
}
|
||||
|
||||
impl PatchItemFilter {
|
||||
pub fn status(&self) -> Option<patch::Status> {
|
||||
self.status
|
||||
}
|
||||
|
||||
pub fn matches(&self, patch: &PatchItem) -> bool {
|
||||
use fuzzy_matcher::skim::SkimMatcherV2;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
|
||||
let matcher = SkimMatcherV2::default();
|
||||
|
||||
let matches_state = match self.status {
|
||||
Some(patch::Status::Draft) => matches!(patch.state, patch::State::Draft),
|
||||
Some(patch::Status::Open) => matches!(patch.state, patch::State::Open { .. }),
|
||||
Some(patch::Status::Merged) => matches!(patch.state, patch::State::Merged { .. }),
|
||||
Some(patch::Status::Archived) => matches!(patch.state, patch::State::Archived),
|
||||
None => true,
|
||||
};
|
||||
|
||||
let matches_authored = if self.authored {
|
||||
patch.author.you
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let matches_authors = (!self.authors.is_empty())
|
||||
.then(|| {
|
||||
self.authors
|
||||
.iter()
|
||||
.any(|other| patch.author.nid.unwrap() == **other)
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
let matches_search = match &self.search {
|
||||
Some(search) => match matcher.fuzzy_match(&patch.title, search) {
|
||||
Some(score) => score == 0 || score > 60,
|
||||
_ => false,
|
||||
},
|
||||
None => true,
|
||||
};
|
||||
|
||||
matches_state && matches_authored && matches_authors && matches_search
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PatchItemFilter {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
let mut status = None;
|
||||
let mut search = String::new();
|
||||
let mut authored = false;
|
||||
let mut authors = vec![];
|
||||
|
||||
let mut authors_parser = |input| -> IResult<&str, Vec<&str>> {
|
||||
preceded(
|
||||
tag("authors:"),
|
||||
delimited(
|
||||
tag("["),
|
||||
separated_list0(tag(","), take(56_usize)),
|
||||
tag("]"),
|
||||
),
|
||||
)(input)
|
||||
};
|
||||
|
||||
let parts = value.split(' ');
|
||||
for part in parts {
|
||||
match part {
|
||||
"is:open" => status = Some(patch::Status::Open),
|
||||
"is:merged" => status = Some(patch::Status::Merged),
|
||||
"is:archived" => status = Some(patch::Status::Archived),
|
||||
"is:draft" => status = Some(patch::Status::Draft),
|
||||
"is:authored" => authored = true,
|
||||
other => match authors_parser.parse(other) {
|
||||
Ok((_, dids)) => {
|
||||
for did in dids {
|
||||
authors.push(Did::from_str(did)?);
|
||||
}
|
||||
}
|
||||
_ => search.push_str(other),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
status,
|
||||
authored,
|
||||
authors,
|
||||
search: Some(search),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_issue_state(state: &issue::State) -> (String, Color) {
|
||||
match state {
|
||||
issue::State::Open => (" ● ".into(), Color::Green),
|
||||
@@ -551,3 +660,30 @@ pub fn format_assignees(assignees: &[(Option<PublicKey>, Option<Alias>, bool)])
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn patch_item_filter_from_str_should_succeed() -> Result<()> {
|
||||
let search = r#"is:open is:authored authors:[did:key:z6MkkpTPzcq1ybmjQyQpyre15JUeMvZY6toxoZVpLZ8YarsB,did:key:z6Mku8hpprWTmCv3BqkssCYDfr2feUdyLSUnycVajFo9XVAx] cli"#;
|
||||
let actual = PatchItemFilter::from_str(search)?;
|
||||
|
||||
let expected = PatchItemFilter {
|
||||
status: Some(patch::Status::Open),
|
||||
authored: true,
|
||||
authors: vec![
|
||||
Did::from_str("did:key:z6MkkpTPzcq1ybmjQyQpyre15JUeMvZY6toxoZVpLZ8YarsB")?,
|
||||
Did::from_str("did:key:z6Mku8hpprWTmCv3BqkssCYDfr2feUdyLSUnycVajFo9XVAx")?,
|
||||
],
|
||||
search: Some("cli".to_string()),
|
||||
};
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user