radicle: add `list_by_status` method to `cob::issue::Issues`
Provides the same `list_by_status` and `<status>` methods that `cob::patch::Patches` has to `Issues`
This commit is contained in:
parent
7c902b6905
commit
985987468d
|
|
@ -13,7 +13,7 @@ use crate::crypto::Signer;
|
||||||
use crate::prelude::{Did, RepoId};
|
use crate::prelude::{Did, RepoId};
|
||||||
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};
|
use crate::storage::{HasRepoId, ReadRepository, RepositoryError, SignRepository, WriteRepository};
|
||||||
|
|
||||||
use super::{Issue, IssueCounts, IssueId, IssueMut, State};
|
use super::{CloseReason, Issue, IssueCounts, IssueId, IssueMut, State};
|
||||||
|
|
||||||
/// A set of read-only methods for a [`Issue`] store.
|
/// A set of read-only methods for a [`Issue`] store.
|
||||||
pub trait Issues {
|
pub trait Issues {
|
||||||
|
|
@ -31,9 +31,33 @@ pub trait Issues {
|
||||||
/// List all issues that are in the store.
|
/// List all issues that are in the store.
|
||||||
fn list(&self) -> Result<Self::Iter<'_>, Self::Error>;
|
fn list(&self) -> Result<Self::Iter<'_>, Self::Error>;
|
||||||
|
|
||||||
|
/// List all issues in the store that match the provided `status`.
|
||||||
|
///
|
||||||
|
/// Also see [`Issues::opened`], [`Issues::solved`] and [`Issues::closed`].
|
||||||
|
fn list_by_status(&self, status: &State) -> Result<Self::Iter<'_>, Self::Error>;
|
||||||
|
|
||||||
/// Get the [`IssueCounts`] of all the issues in the store.
|
/// Get the [`IssueCounts`] of all the issues in the store.
|
||||||
fn counts(&self) -> Result<IssueCounts, Self::Error>;
|
fn counts(&self) -> Result<IssueCounts, Self::Error>;
|
||||||
|
|
||||||
|
/// List all open issues in the store.
|
||||||
|
fn opened(&self) -> Result<Self::Iter<'_>, Self::Error> {
|
||||||
|
self.list_by_status(&State::Open)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all closed issues with `CloseReason::Solved` in the store.
|
||||||
|
fn solved(&self) -> Result<Self::Iter<'_>, Self::Error> {
|
||||||
|
self.list_by_status(&State::Closed {
|
||||||
|
reason: CloseReason::Solved,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all closed issues with `CloseReasion::Other` in the store.
|
||||||
|
fn closed(&self) -> Result<Self::Iter<'_>, Self::Error> {
|
||||||
|
self.list_by_status(&State::Closed {
|
||||||
|
reason: CloseReason::Other,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if there are no issues in the store.
|
/// Returns `true` if there are no issues in the store.
|
||||||
fn is_empty(&self) -> Result<bool, Self::Error> {
|
fn is_empty(&self) -> Result<bool, Self::Error> {
|
||||||
Ok(self.counts()?.total() == 0)
|
Ok(self.counts()?.total() == 0)
|
||||||
|
|
@ -368,6 +392,19 @@ where
|
||||||
.map_err(super::Error::from)
|
.map_err(super::Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn list_by_status(&self, status: &State) -> Result<Self::Iter<'_>, Self::Error> {
|
||||||
|
let status = *status;
|
||||||
|
self.store
|
||||||
|
.all()
|
||||||
|
.map(move |inner| NoCacheIter {
|
||||||
|
inner: Box::new(inner.into_iter().filter_map(move |res| match res {
|
||||||
|
Ok((id, issue)) => (status == issue.state).then_some((id, issue)).map(Ok),
|
||||||
|
Err(e) => Some(Err(e.into())),
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
.map_err(super::Error::from)
|
||||||
|
}
|
||||||
|
|
||||||
fn counts(&self) -> Result<IssueCounts, Self::Error> {
|
fn counts(&self) -> Result<IssueCounts, Self::Error> {
|
||||||
self.store.counts().map_err(super::Error::from)
|
self.store.counts().map_err(super::Error::from)
|
||||||
}
|
}
|
||||||
|
|
@ -425,6 +462,10 @@ where
|
||||||
query::list(&self.cache.db, &self.rid())
|
query::list(&self.cache.db, &self.rid())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn list_by_status(&self, status: &State) -> Result<Self::Iter<'_>, Self::Error> {
|
||||||
|
query::list_by_status(&self.cache.db, &self.rid(), status)
|
||||||
|
}
|
||||||
|
|
||||||
fn counts(&self) -> Result<IssueCounts, Self::Error> {
|
fn counts(&self) -> Result<IssueCounts, Self::Error> {
|
||||||
query::counts(&self.cache.db, &self.rid())
|
query::counts(&self.cache.db, &self.rid())
|
||||||
}
|
}
|
||||||
|
|
@ -445,6 +486,10 @@ where
|
||||||
query::list(&self.cache.db, &self.rid())
|
query::list(&self.cache.db, &self.rid())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn list_by_status(&self, status: &State) -> Result<Self::Iter<'_>, Self::Error> {
|
||||||
|
query::list_by_status(&self.cache.db, &self.rid(), status)
|
||||||
|
}
|
||||||
|
|
||||||
fn counts(&self) -> Result<IssueCounts, Self::Error> {
|
fn counts(&self) -> Result<IssueCounts, Self::Error> {
|
||||||
query::counts(&self.cache.db, &self.rid())
|
query::counts(&self.cache.db, &self.rid())
|
||||||
}
|
}
|
||||||
|
|
@ -497,6 +542,26 @@ mod query {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn list_by_status<'a>(
|
||||||
|
db: &'a sql::ConnectionThreadSafe,
|
||||||
|
rid: &RepoId,
|
||||||
|
filter: &State,
|
||||||
|
) -> Result<IssuesIter<'a>, Error> {
|
||||||
|
let mut stmt = db.prepare(
|
||||||
|
"SELECT id, issue
|
||||||
|
FROM issues
|
||||||
|
WHERE repo = ?1
|
||||||
|
AND issue->>'$.state.status' = ?2
|
||||||
|
ORDER BY id
|
||||||
|
",
|
||||||
|
)?;
|
||||||
|
stmt.bind((1, rid))?;
|
||||||
|
stmt.bind((2, sql::Value::String(filter.to_string())))?;
|
||||||
|
Ok(IssuesIter {
|
||||||
|
inner: stmt.into_iter(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn counts(
|
pub(super) fn counts(
|
||||||
db: &sql::ConnectionThreadSafe,
|
db: &sql::ConnectionThreadSafe,
|
||||||
rid: &RepoId,
|
rid: &RepoId,
|
||||||
|
|
@ -678,6 +743,36 @@ mod tests {
|
||||||
assert_eq!(issues, list);
|
assert_eq!(issues, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_by_status() {
|
||||||
|
let repo = arbitrary::gen::<MockRepository>(1);
|
||||||
|
let mut cache = memory(repo);
|
||||||
|
let ids = (0..arbitrary::gen::<u8>(1))
|
||||||
|
.map(|_| IssueId::from(arbitrary::oid()))
|
||||||
|
.collect::<BTreeSet<IssueId>>();
|
||||||
|
let mut issues = Vec::with_capacity(ids.len());
|
||||||
|
|
||||||
|
for id in ids.iter() {
|
||||||
|
let issue = Issue {
|
||||||
|
title: id.to_string(),
|
||||||
|
..Issue::new(Thread::default())
|
||||||
|
};
|
||||||
|
cache
|
||||||
|
.update(&cache.rid(), &IssueId::from(*id), &issue)
|
||||||
|
.unwrap();
|
||||||
|
issues.push((*id, issue));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut list = cache
|
||||||
|
.list_by_status(&State::Open)
|
||||||
|
.unwrap()
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.unwrap();
|
||||||
|
list.sort_by_key(|(id, _)| *id);
|
||||||
|
issues.sort_by_key(|(id, _)| *id);
|
||||||
|
assert_eq!(issues, list);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_remove() {
|
fn test_remove() {
|
||||||
let repo = arbitrary::gen::<MockRepository>(1);
|
let repo = arbitrary::gen::<MockRepository>(1);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue