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