1use derive_more::Display;
4use paperless_api_macros::{CreateDto, Item, 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)]
55#[serde(rename_all = "camelCase")]
56pub enum DisplayMode {
57 Table,
58 SmallCards,
59 LargeCards,
60}
61
62#[derive(Debug, Clone, Deserialize, Serialize)]
63pub struct FilterRule {
64 #[serde(rename = "rule_type")]
65 pub rule: FilterRuleType,
66
67 pub value: Option<String>,
68}
69
70#[derive(Debug, Clone, Copy, Display)]
71#[repr(u8)]
72pub enum FilterRuleType {
73 TitleContains = 0,
74 ContentContains = 1,
75 AsnIs = 2,
76 CorrespondentIs = 3,
77 DocumentTypeIs = 4,
78 IsInInbox = 5,
79 HasTag = 6,
80 HasAnyTag = 7,
81 CreatedBefore = 8,
82 CreatedAfter = 9,
83 CreatedInYear = 10,
84 CreatedInMonth = 11,
85 CreatedDayIs = 12,
86 AddedBefore = 13,
87 AddedAfter = 14,
88 ModifiedBefore = 15,
89 ModifiedAfter = 16,
90 DoesNotHaveTag = 17,
91 DocumentHasNoAsn = 18,
92 TitleOrContentContains = 19,
93 FullTextSearch = 20,
94 SimilarDocuments = 21,
95 HasTagsIn = 22,
96 AsnGreaterThan = 23,
97 AsnLessThan = 24,
98 StoragePathIs = 25,
99 HasCorrespondentIn = 26,
100 HasNoCorrespondentIn = 27,
101 HasDocumentTypeIn = 28,
102 HasNoDocumentTypeIn = 29,
103 HasStoragePathIn = 30,
104 HasNoStoragePathIn = 31,
105 OwnerIs = 32,
106 HasOwnerIn = 33,
107 HasNoOwner = 34,
108 HasNoOwnerIn = 35,
109 HasCustomFieldValue = 36,
110 IsSharedByMe = 37,
111 HasCustomFields = 38,
112 HasTheCustomFields = 39,
113 DoesNotHaveCustomFields = 40,
114 DoesNotHaveCustomField = 41,
115 CustomFieldQuery = 42,
116 CreateDto = 43,
117 CreatedBy = 44,
118 AddedTo = 45,
119 AddedBy = 46,
120 MimeTypeIs = 47,
121
122 Unknown(u8),
123}
124
125impl Serialize for FilterRuleType {
126 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127 where
128 S: serde::Serializer,
129 {
130 let value = if let FilterRuleType::Unknown(unknown) = self {
131 *unknown
132 } else {
133 unsafe { *(std::ptr::from_ref::<FilterRuleType>(self)).cast::<u8>() }
135 };
136
137 serializer.serialize_u8(value)
138 }
139}
140
141impl<'de> serde::Deserialize<'de> for FilterRuleType {
142 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
143 where
144 D: serde::Deserializer<'de>,
145 {
146 let value = u8::deserialize(deserializer)?;
147
148 let enum_value = match value {
149 0..=47 => unsafe { std::mem::transmute::<u16, FilterRuleType>(u16::from(value)) },
150 _ => FilterRuleType::Unknown(value),
151 };
152
153 Ok(enum_value)
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn filter_rule_type_boundary_values() {
163 let zero: FilterRuleType = serde_json::from_str("0").unwrap();
164 assert!(matches!(zero, FilterRuleType::TitleContains));
165
166 let forty_seven: FilterRuleType = serde_json::from_str("47").unwrap();
167 assert!(matches!(forty_seven, FilterRuleType::MimeTypeIs));
168
169 let forty_seven: FilterRuleType = serde_json::from_str("48").unwrap();
170 assert!(matches!(forty_seven, FilterRuleType::Unknown(48)));
171 }
172
173 #[test]
174 fn filter_rule_type_unknown_value_roundtrip() {
175 let unknown: FilterRuleType = serde_json::from_str("200").unwrap();
176 assert!(matches!(unknown, FilterRuleType::Unknown(200)));
177
178 let serialized = serde_json::to_string(&unknown).unwrap();
179 assert_eq!(serialized, "200");
180 }
181
182 #[test]
183 fn filter_rule_roundtrip_with_value() {
184 let rule = FilterRule {
185 rule: FilterRuleType::FullTextSearch,
186 value: Some("created:[-3 month to now]".to_string()),
187 };
188
189 let json = serde_json::to_string(&rule).unwrap();
190 let deserialized: FilterRule = serde_json::from_str(&json).unwrap();
191
192 assert!(matches!(deserialized.rule, FilterRuleType::FullTextSearch));
193 assert_eq!(
194 deserialized.value,
195 Some("created:[-3 month to now]".to_string())
196 );
197 }
198}