use std::str::FromStr;
use super::{
Issue,
data::{label::Label, status::Status},
};
use crate::{
entities::identity::Identity,
query::queryable::{QueryKeyValue, Queryable},
replica::{
Replica,
entity::{Entity, identity::IdentityStub, snapshot::Snapshot},
},
};
impl Queryable for Snapshot<Issue> {
type KeyValue = MatchKeyValue;
fn matches(&self, key: &Self::KeyValue) -> bool {
match key {
MatchKeyValue::Status(status) => self.status() == *status,
MatchKeyValue::Author { resolved_id, .. } => self.author() == *resolved_id,
MatchKeyValue::Participant { resolved_id, .. } => {
self.participants().any(|other| other == *resolved_id)
}
MatchKeyValue::Label(label) => self.labels().contains(label),
MatchKeyValue::Title(search) => self.title().contains(search),
MatchKeyValue::Empty(value) => match value {
EmptyValue::Labels => self.labels().is_empty(),
},
MatchKeyValue::Search(search) => {
self.body().contains(search) || self.title().contains(search)
}
MatchKeyValue::Body(search) => self.body().contains(search),
}
}
}
#[derive(Debug, Clone)]
pub enum MatchKeyValue {
Status(Status),
Author {
resolved_id: IdentityStub,
name: String,
},
Participant {
resolved_id: IdentityStub,
name: String,
},
Label(Label),
Empty(EmptyValue),
Title(String),
Body(String),
Search(String),
}
#[allow(missing_docs)]
pub mod decode {
use crate::entities::{identity::Identity, issue::data::status};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Unknown Issue match key: {0}")]
UnknownKey(String),
#[error("Failed to parse the status value: {0}")]
UnknowStatusValue(#[from] status::decode::Error),
#[error("Failed to parse the empty value: {0} (valid ones are: 'labels')")]
UnknownEmptyValue(String),
#[error("Failed to read an Identity specified via the author or participant key: {0}")]
IdentityRead(#[from] crate::replica::entity::read::Error<Identity>),
#[error(
"Failed to get the reference of an Identity specified via the author or participant \
key: {0}"
)]
IdentityGet(#[from] crate::replica::get::Error),
#[error("The identity ('{0}') name for the author or participant key was not found.")]
NoIdentityMatches(String),
}
}
#[derive(Debug, Clone, Copy)]
pub enum EmptyValue {
Labels,
}
impl FromStr for EmptyValue {
type Err = decode::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = match s {
"labels" => Self::Labels,
other => return Err(decode::Error::UnknownEmptyValue(other.to_owned())),
};
Ok(value)
}
}
impl QueryKeyValue for MatchKeyValue {
type Err = decode::Error;
type UserState = Replica;
fn from_key_value(
user_state: &Self::UserState,
key: &str,
value: String,
) -> Result<Self, Self::Err> {
fn get_identity_stub_by_name(
user_state: &Replica,
value: &str,
) -> Result<(IdentityStub, String), decode::Error> {
match user_state
.get_all::<Identity>()?
.find_map(|mm_identity| match mm_identity {
Ok(m_identity) => match m_identity {
Ok(identity) => {
let snapshot = identity.snapshot();
if snapshot.name().contains(value)
|| snapshot.login_name().is_some_and(|v| v.contains(value))
{
Some(Ok::<_, decode::Error>((
IdentityStub { id: identity.id() },
snapshot.name().to_owned(),
)))
} else {
None
}
}
Err(err) => Some(Err(err.into())),
},
Err(err) => Some(Err(err.into())),
}) {
Some(val) => val,
None => Err(decode::Error::NoIdentityMatches(value.to_owned())),
}
}
match key {
"status" => Ok(MatchKeyValue::Status(Status::from_str(&value)?)),
"author" => {
let (resolved_id, name) = get_identity_stub_by_name(user_state, &value)?;
Ok(MatchKeyValue::Author { resolved_id, name })
}
"participant" => {
let (resolved_id, name) = get_identity_stub_by_name(user_state, &value)?;
Ok(MatchKeyValue::Participant { resolved_id, name })
}
"label" => Ok(MatchKeyValue::Label(Label::from(value.as_str()))),
"title" => Ok(MatchKeyValue::Title(value)),
"empty" => Ok(MatchKeyValue::Empty(EmptyValue::from_str(&value)?)),
"search" => Ok(MatchKeyValue::Search(value)),
"body" => Ok(MatchKeyValue::Body(value)),
_ => Err(decode::Error::UnknownKey(key.to_owned())),
}
}
fn from_value(user_state: &Self::UserState, value: String) -> Result<Self, Self::Err>
where
Self: Sized,
{
Self::from_key_value(user_state, "search", value)
}
fn to_key_and_value(&self) -> (&str, &str)
where
Self: Sized,
{
match self {
MatchKeyValue::Status(status) => {
let status_str = match status {
Status::Open => "open",
Status::Closed => "closed",
};
("status", status_str)
}
MatchKeyValue::Author { name, .. } => ("author", name.as_str()),
MatchKeyValue::Participant { name, .. } => ("participant", name.as_str()),
MatchKeyValue::Label(label) => ("label", label.0.as_str()),
MatchKeyValue::Title(search) => ("title", search.as_str()),
MatchKeyValue::Empty(empty_value) => {
let empty_value_str = match empty_value {
EmptyValue::Labels => "labels",
};
("empty", empty_value_str)
}
MatchKeyValue::Body(search) => ("body", search.as_str()),
MatchKeyValue::Search(search) => ("search", search.as_str()),
}
}
}