1use bon::Builder;
7use serde::Serialize;
8
9#[derive(Debug, Clone, Default, Serialize, Builder)]
11#[serde(rename_all = "camelCase")]
12#[non_exhaustive]
13pub struct StringComparator {
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub eq: Option<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub neq: Option<String>,
20 #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
22 pub r#in: Option<Vec<String>>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub nin: Option<Vec<String>>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub contains: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub not_contains: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub contains_ignore_case: Option<String>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub starts_with: Option<String>,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub ends_with: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub eq_ignore_case: Option<String>,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub neq_ignore_case: Option<String>,
47}
48
49#[derive(Debug, Clone, Default, Serialize, Builder)]
51#[serde(rename_all = "camelCase")]
52#[non_exhaustive]
53pub struct IdComparator {
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub eq: Option<String>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub neq: Option<String>,
60 #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
62 pub r#in: Option<Vec<String>>,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub nin: Option<Vec<String>>,
66}
67
68#[derive(Debug, Clone, Default, Serialize, Builder)]
70#[serde(rename_all = "camelCase")]
71#[non_exhaustive]
72pub struct NumberComparator {
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub eq: Option<f64>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub neq: Option<f64>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub lt: Option<f64>,
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub lte: Option<f64>,
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub gt: Option<f64>,
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub gte: Option<f64>,
91 #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
93 pub r#in: Option<Vec<f64>>,
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub nin: Option<Vec<f64>>,
97}
98
99#[derive(Debug, Clone, Default, Serialize, Builder)]
101#[serde(rename_all = "camelCase")]
102#[non_exhaustive]
103pub struct NullableNumberComparator {
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub eq: Option<f64>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub neq: Option<f64>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub lt: Option<f64>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub lte: Option<f64>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub gt: Option<f64>,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub gte: Option<f64>,
122 #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
124 pub r#in: Option<Vec<f64>>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub nin: Option<Vec<f64>>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub null: Option<bool>,
131}
132
133#[derive(Debug, Clone, Default, Serialize, Builder)]
136#[serde(rename_all = "camelCase")]
137#[non_exhaustive]
138pub struct DateComparator {
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub eq: Option<String>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub neq: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub lt: Option<String>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub lte: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub gt: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub gte: Option<String>,
157}
158
159#[derive(Debug, Clone, Default, Serialize, Builder)]
161#[serde(rename_all = "camelCase")]
162#[non_exhaustive]
163pub struct BooleanComparator {
164 #[serde(skip_serializing_if = "Option::is_none")]
166 pub eq: Option<bool>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub neq: Option<bool>,
170}
171
172#[derive(Debug, Clone, Default, Serialize, Builder)]
174#[serde(rename_all = "camelCase")]
175#[non_exhaustive]
176pub struct RelationExistsComparator {
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub eq: Option<bool>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub neq: Option<bool>,
183}
184
185#[derive(Debug, Clone, Default, Serialize, Builder)]
187#[serde(rename_all = "camelCase")]
188#[non_exhaustive]
189pub struct IssueFilter {
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub and: Option<Vec<IssueFilter>>,
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub or: Option<Vec<IssueFilter>>,
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub title: Option<StringComparator>,
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub description: Option<StringComparator>,
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub number: Option<NumberComparator>,
205 #[serde(skip_serializing_if = "Option::is_none")]
207 pub priority: Option<NullableNumberComparator>,
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub estimate: Option<NullableNumberComparator>,
211 #[serde(skip_serializing_if = "Option::is_none")]
213 pub due_date: Option<DateComparator>,
214 #[serde(skip_serializing_if = "Option::is_none")]
216 pub created_at: Option<DateComparator>,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub updated_at: Option<DateComparator>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub completed_at: Option<DateComparator>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub state: Option<WorkflowStateFilter>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub team: Option<TeamFilter>,
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub assignee: Option<UserFilter>,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub creator: Option<UserFilter>,
235 #[serde(skip_serializing_if = "Option::is_none")]
237 pub labels: Option<IssueLabelFilter>,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub project: Option<ProjectFilter>,
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub project_milestone: Option<ProjectMilestoneFilter>,
244 #[serde(skip_serializing_if = "Option::is_none")]
246 #[builder(into)]
247 pub parent: Option<Box<IssueFilter>>,
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub has_blocked_by_relations: Option<RelationExistsComparator>,
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub has_blocking_relations: Option<RelationExistsComparator>,
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub has_duplicate_relations: Option<RelationExistsComparator>,
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub has_related_relations: Option<RelationExistsComparator>,
260}
261
262#[derive(Debug, Clone, Default, Serialize, Builder)]
264#[serde(rename_all = "camelCase")]
265#[non_exhaustive]
266pub struct TeamFilter {
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub and: Option<Vec<TeamFilter>>,
270 #[serde(skip_serializing_if = "Option::is_none")]
272 pub or: Option<Vec<TeamFilter>>,
273 #[serde(skip_serializing_if = "Option::is_none")]
275 pub id: Option<IdComparator>,
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub key: Option<StringComparator>,
279 #[serde(skip_serializing_if = "Option::is_none")]
281 pub name: Option<StringComparator>,
282}
283
284#[derive(Debug, Clone, Default, Serialize, Builder)]
286#[serde(rename_all = "camelCase")]
287#[non_exhaustive]
288pub struct UserFilter {
289 #[serde(skip_serializing_if = "Option::is_none")]
291 pub and: Option<Vec<UserFilter>>,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub or: Option<Vec<UserFilter>>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub id: Option<IdComparator>,
298 #[serde(skip_serializing_if = "Option::is_none")]
300 pub name: Option<StringComparator>,
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub display_name: Option<StringComparator>,
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub email: Option<StringComparator>,
307 #[serde(skip_serializing_if = "Option::is_none")]
309 pub active: Option<BooleanComparator>,
310}
311
312#[derive(Debug, Clone, Default, Serialize, Builder)]
314#[serde(rename_all = "camelCase")]
315#[non_exhaustive]
316pub struct WorkflowStateFilter {
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub and: Option<Vec<WorkflowStateFilter>>,
320 #[serde(skip_serializing_if = "Option::is_none")]
322 pub or: Option<Vec<WorkflowStateFilter>>,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub id: Option<IdComparator>,
326 #[serde(skip_serializing_if = "Option::is_none")]
328 pub name: Option<StringComparator>,
329 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
332 pub r#type: Option<StringComparator>,
333 #[serde(skip_serializing_if = "Option::is_none")]
335 pub team: Option<TeamFilter>,
336}
337
338#[derive(Debug, Clone, Default, Serialize, Builder)]
340#[serde(rename_all = "camelCase")]
341#[non_exhaustive]
342pub struct IssueLabelFilter {
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub and: Option<Vec<IssueLabelFilter>>,
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub or: Option<Vec<IssueLabelFilter>>,
349 #[serde(skip_serializing_if = "Option::is_none")]
351 pub id: Option<IdComparator>,
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub name: Option<StringComparator>,
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub team: Option<TeamFilter>,
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub creator: Option<UserFilter>,
361}
362
363#[derive(Debug, Clone, Default, Serialize, Builder)]
365#[serde(rename_all = "camelCase")]
366#[non_exhaustive]
367pub struct ProjectFilter {
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub and: Option<Vec<ProjectFilter>>,
371 #[serde(skip_serializing_if = "Option::is_none")]
373 pub or: Option<Vec<ProjectFilter>>,
374 #[serde(skip_serializing_if = "Option::is_none")]
376 pub id: Option<IdComparator>,
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub name: Option<StringComparator>,
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub state: Option<StringComparator>,
383 #[serde(skip_serializing_if = "Option::is_none")]
385 pub health: Option<StringComparator>,
386 #[serde(skip_serializing_if = "Option::is_none")]
388 pub priority: Option<NullableNumberComparator>,
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub start_date: Option<DateComparator>,
392 #[serde(skip_serializing_if = "Option::is_none")]
394 pub target_date: Option<DateComparator>,
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub created_at: Option<DateComparator>,
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub updated_at: Option<DateComparator>,
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub lead: Option<UserFilter>,
404}
405
406#[derive(Debug, Clone, Default, Serialize, Builder)]
408#[serde(rename_all = "camelCase")]
409#[non_exhaustive]
410pub struct ProjectMilestoneFilter {
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub and: Option<Vec<ProjectMilestoneFilter>>,
414 #[serde(skip_serializing_if = "Option::is_none")]
416 pub or: Option<Vec<ProjectMilestoneFilter>>,
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub id: Option<IdComparator>,
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub name: Option<StringComparator>,
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub target_date: Option<DateComparator>,
426}
427
428#[derive(Debug, Clone, Default, Serialize, Builder)]
430#[serde(rename_all = "camelCase")]
431#[non_exhaustive]
432pub struct CommentFilter {
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub and: Option<Vec<CommentFilter>>,
436 #[serde(skip_serializing_if = "Option::is_none")]
438 pub or: Option<Vec<CommentFilter>>,
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub id: Option<IdComparator>,
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub body: Option<StringComparator>,
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub user: Option<UserFilter>,
448 #[serde(skip_serializing_if = "Option::is_none")]
450 #[builder(into)]
451 pub issue: Option<Box<IssueFilter>>,
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub created_at: Option<DateComparator>,
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460
461 #[test]
462 fn filters_skip_unset_fields_and_rename_in() {
463 let filter = IssueFilter::builder()
464 .title(
465 StringComparator::builder()
466 .contains("bug".to_string())
467 .build(),
468 )
469 .state(
470 WorkflowStateFilter::builder()
471 .r#type(
472 StringComparator::builder()
473 .r#in(vec!["started".to_string(), "unstarted".to_string()])
474 .build(),
475 )
476 .build(),
477 )
478 .parent(IssueFilter::default())
479 .build();
480 let value = serde_json::to_value(&filter).unwrap();
481 assert_eq!(
482 value,
483 serde_json::json!({
484 "title": { "contains": "bug" },
485 "state": { "type": { "in": ["started", "unstarted"] } },
486 "parent": {},
487 })
488 );
489 }
490}