git_bug/entities/issue/query/
mod.rs1use std::str::FromStr;
14
15use super::{
16 Issue,
17 data::{label::Label, status::Status},
18};
19use crate::{
20 entities::identity::Identity,
21 query::queryable::{QueryKeyValue, Queryable},
22 replica::{
23 Replica,
24 entity::{Entity, identity::IdentityStub, snapshot::Snapshot},
25 },
26};
27
28impl Queryable for Snapshot<Issue> {
29 type KeyValue = MatchKeyValue;
30
31 fn matches(&self, key: &Self::KeyValue) -> bool {
32 match key {
33 MatchKeyValue::Status(status) => self.status() == *status,
34 MatchKeyValue::Author { resolved_id, .. } => self.author() == *resolved_id,
35 MatchKeyValue::Participant { resolved_id, .. } => {
36 self.participants().any(|other| other == *resolved_id)
37 }
38 MatchKeyValue::Label(label) => self.labels().contains(label),
39 MatchKeyValue::Title(search) => self.title().contains(search),
40 MatchKeyValue::Empty(value) => match value {
41 EmptyValue::Labels => self.labels().is_empty(),
42 },
43 MatchKeyValue::Search(search) => {
44 self.body().contains(search) || self.title().contains(search)
45 }
46 MatchKeyValue::Body(search) => self.body().contains(search),
47 }
48 }
49}
50
51#[derive(Debug, Clone)]
94pub enum MatchKeyValue {
95 Status(Status),
97
98 Author {
100 resolved_id: IdentityStub,
104
105 name: String,
109 },
110
111 Participant {
113 resolved_id: IdentityStub,
117
118 name: String,
122 },
123
124 Label(Label),
126 Empty(EmptyValue),
128
129 Title(String),
131
132 Body(String),
134
135 Search(String),
140}
141
142#[allow(missing_docs)]
143pub mod decode {
144 use crate::entities::{identity::Identity, issue::data::status};
145
146 #[derive(Debug, thiserror::Error)]
147 pub enum Error {
148 #[error("Unknown Issue match key: {0}")]
149 UnknownKey(String),
150
151 #[error("Failed to parse the status value: {0}")]
152 UnknowStatusValue(#[from] status::decode::Error),
153
154 #[error("Failed to parse the empty value: {0} (valid ones are: 'labels')")]
155 UnknownEmptyValue(String),
156
157 #[error("Failed to read an Identity specified via the author or participant key: {0}")]
158 IdentityRead(#[from] crate::replica::entity::read::Error<Identity>),
159
160 #[error(
161 "Failed to get the reference of an Identity specified via the author or participant \
162 key: {0}"
163 )]
164 IdentityGet(#[from] crate::replica::get::Error),
165
166 #[error("The identity ('{0}') name for the author or participant key was not found.")]
167 NoIdentityMatches(String),
168 }
169}
170
171#[derive(Debug, Clone, Copy)]
173pub enum EmptyValue {
174 Labels,
176}
177impl FromStr for EmptyValue {
178 type Err = decode::Error;
179
180 fn from_str(s: &str) -> Result<Self, Self::Err> {
181 let value = match s {
182 "labels" => Self::Labels,
183 other => return Err(decode::Error::UnknownEmptyValue(other.to_owned())),
184 };
185 Ok(value)
186 }
187}
188
189impl QueryKeyValue for MatchKeyValue {
190 type Err = decode::Error;
191 type UserState = Replica;
192
193 fn from_key_value(
194 user_state: &Self::UserState,
195 key: &str,
196 value: String,
197 ) -> Result<Self, Self::Err> {
198 fn get_identity_stub_by_name(
199 user_state: &Replica,
200 value: &str,
201 ) -> Result<(IdentityStub, String), decode::Error> {
202 match user_state
203 .get_all::<Identity>()?
204 .find_map(|mm_identity| match mm_identity {
205 Ok(m_identity) => match m_identity {
206 Ok(identity) => {
207 let snapshot = identity.snapshot();
208 if snapshot.name().contains(value)
209 || snapshot.login_name().is_some_and(|v| v.contains(value))
210 {
211 Some(Ok::<_, decode::Error>((
212 IdentityStub { id: identity.id() },
213 snapshot.name().to_owned(),
214 )))
215 } else {
216 None
217 }
218 }
219 Err(err) => Some(Err(err.into())),
220 },
221 Err(err) => Some(Err(err.into())),
222 }) {
223 Some(val) => val,
224 None => Err(decode::Error::NoIdentityMatches(value.to_owned())),
225 }
226 }
227
228 match key {
229 "status" => Ok(MatchKeyValue::Status(Status::from_str(&value)?)),
230 "author" => {
231 let (resolved_id, name) = get_identity_stub_by_name(user_state, &value)?;
232 Ok(MatchKeyValue::Author { resolved_id, name })
233 }
234 "participant" => {
235 let (resolved_id, name) = get_identity_stub_by_name(user_state, &value)?;
236 Ok(MatchKeyValue::Participant { resolved_id, name })
237 }
238 "label" => Ok(MatchKeyValue::Label(Label::from(value.as_str()))),
239 "title" => Ok(MatchKeyValue::Title(value)),
240 "empty" => Ok(MatchKeyValue::Empty(EmptyValue::from_str(&value)?)),
241 "search" => Ok(MatchKeyValue::Search(value)),
242 "body" => Ok(MatchKeyValue::Body(value)),
243 _ => Err(decode::Error::UnknownKey(key.to_owned())),
244 }
245 }
246
247 fn from_value(user_state: &Self::UserState, value: String) -> Result<Self, Self::Err>
248 where
249 Self: Sized,
250 {
251 Self::from_key_value(user_state, "search", value)
252 }
253
254 fn to_key_and_value(&self) -> (&str, &str)
255 where
256 Self: Sized,
257 {
258 match self {
259 MatchKeyValue::Status(status) => {
260 let status_str = match status {
261 Status::Open => "open",
262 Status::Closed => "closed",
263 };
264
265 ("status", status_str)
266 }
267 MatchKeyValue::Author { name, .. } => ("author", name.as_str()),
268 MatchKeyValue::Participant { name, .. } => ("participant", name.as_str()),
269 MatchKeyValue::Label(label) => ("label", label.0.as_str()),
270 MatchKeyValue::Title(search) => ("title", search.as_str()),
271 MatchKeyValue::Empty(empty_value) => {
272 let empty_value_str = match empty_value {
273 EmptyValue::Labels => "labels",
274 };
275 ("empty", empty_value_str)
276 }
277 MatchKeyValue::Body(search) => ("body", search.as_str()),
278 MatchKeyValue::Search(search) => ("search", search.as_str()),
279 }
280 }
281}