1use derive_more::Display;
4use paperless_api_macros::{CreateDto, Item, ReprSerde, UpdateDto};
5use serde::{Deserialize, Serialize};
6
7use crate::metadata::permission::ItemPermissions;
8
9#[derive(Debug, Default, Clone, Deserialize, Serialize, CreateDto, UpdateDto, Item)]
11pub struct SavedView {
12 #[dto(skip)]
14 pub id: crate::id::SavedViewId,
15
16 pub name: String,
18
19 pub sort_field: Option<String>,
21
22 pub sort_reverse: Option<bool>,
24
25 pub filter_rules: Option<Vec<FilterRule>>,
27
28 pub display_mode: Option<DisplayMode>,
30
31 pub display_fields: Option<Vec<String>>,
33
34 pub page_size: Option<u32>,
36
37 #[dto(skip)]
39 pub owner: Option<crate::id::UserId>,
40
41 #[dto(skip)]
43 #[serde(flatten)]
44 pub permissions: ItemPermissions,
45}
46
47#[derive(Debug, Clone, Deserialize, Serialize)]
49#[serde(rename_all = "camelCase")]
50pub enum DisplayMode {
51 Table,
52 SmallCards,
53 LargeCards,
54}
55
56#[derive(Debug, Clone, Deserialize, Serialize)]
58pub struct FilterRule {
59 #[serde(rename = "rule_type")]
61 pub rule: FilterRuleType,
62
63 pub value: Option<String>,
65}
66
67#[derive(Debug, Clone, Copy, Display, ReprSerde)]
69#[repr(u8)]
70pub enum FilterRuleType {
71 TitleContains = 0,
72 ContentContains = 1,
73 AsnIs = 2,
74 CorrespondentIs = 3,
75 DocumentTypeIs = 4,
76 IsInInbox = 5,
77 HasTag = 6,
78 HasAnyTag = 7,
79 CreatedBefore = 8,
80 CreatedAfter = 9,
81 CreatedInYear = 10,
82 CreatedInMonth = 11,
83 CreatedDayIs = 12,
84 AddedBefore = 13,
85 AddedAfter = 14,
86 ModifiedBefore = 15,
87 ModifiedAfter = 16,
88 DoesNotHaveTag = 17,
89 DocumentHasNoAsn = 18,
90 TitleOrContentContains = 19,
91 FullTextSearch = 20,
92 SimilarDocuments = 21,
93 HasTagsIn = 22,
94 AsnGreaterThan = 23,
95 AsnLessThan = 24,
96 StoragePathIs = 25,
97 HasCorrespondentIn = 26,
98 HasNoCorrespondentIn = 27,
99 HasDocumentTypeIn = 28,
100 HasNoDocumentTypeIn = 29,
101 HasStoragePathIn = 30,
102 HasNoStoragePathIn = 31,
103 OwnerIs = 32,
104 HasOwnerIn = 33,
105 HasNoOwner = 34,
106 HasNoOwnerIn = 35,
107 HasCustomFieldValue = 36,
108 IsSharedByMe = 37,
109 HasCustomFields = 38,
110 HasTheCustomFields = 39,
111 DoesNotHaveCustomFields = 40,
112 DoesNotHaveCustomField = 41,
113 CustomFieldQuery = 42,
114 CreateDto = 43,
115 CreatedBy = 44,
116 AddedTo = 45,
117 AddedBy = 46,
118 MimeTypeIs = 47,
119 SimpleTitleSearch = 48,
120 SimpleTextSearch = 49,
121
122 Unknown(u8),
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn filter_rule_type_boundary_values() {
131 let zero: FilterRuleType = serde_json::from_str("0").unwrap();
132 assert!(matches!(zero, FilterRuleType::TitleContains));
133
134 let last: FilterRuleType = serde_json::from_str("49").unwrap();
135 assert!(matches!(last, FilterRuleType::SimpleTextSearch));
136
137 let first_unknown: FilterRuleType = serde_json::from_str("50").unwrap();
138 assert!(matches!(first_unknown, FilterRuleType::Unknown(50)));
139 }
140
141 #[test]
142 fn filter_rule_type_unknown_value_roundtrip() {
143 let unknown: FilterRuleType = serde_json::from_str("200").unwrap();
144 assert!(matches!(unknown, FilterRuleType::Unknown(200)));
145
146 let serialized = serde_json::to_string(&unknown).unwrap();
147 assert_eq!(serialized, "200");
148 }
149
150 #[test]
151 fn filter_rule_roundtrip_with_value() {
152 let rule = FilterRule {
153 rule: FilterRuleType::FullTextSearch,
154 value: Some("created:[-3 month to now]".to_string()),
155 };
156
157 let json = serde_json::to_string(&rule).unwrap();
158 let deserialized: FilterRule = serde_json::from_str(&json).unwrap();
159
160 assert!(matches!(deserialized.rule, FilterRuleType::FullTextSearch));
161 assert_eq!(
162 deserialized.value,
163 Some("created:[-3 month to now]".to_string())
164 );
165 }
166}