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