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