Skip to main content

paperless_api/
saved_view.rs

1//! Types related to saved views in the paperless UI.
2
3use derive_more::Display;
4use paperless_api_macros::{CreateDto, Item, UpdateDto};
5use serde::{Deserialize, Serialize};
6
7use crate::metadata::permission::ItemPermissions;
8
9/// A saved view in the paperless UI.
10#[derive(Debug, Default, Clone, Deserialize, Serialize, CreateDto, UpdateDto, Item)]
11#[api_info(endpoint = "saved_views")]
12pub struct SavedView {
13    /// The ID of the saved view.
14    #[dto(skip)]
15    pub id: crate::id::SavedViewId,
16
17    /// The name of the saved view.
18    pub name: String,
19
20    /// Whether the saved view should be shown on the dashboard.
21    pub show_on_dashboard: bool,
22
23    /// Whether the saved view should be shown in the sidebar.
24    pub show_in_sidebar: bool,
25
26    /// The field to sort the view by.
27    pub sort_field: Option<String>,
28
29    /// Whether to sort the view in reverse order.
30    pub sort_reverse: Option<bool>,
31
32    /// The filter rules determining which documents are shown in the view.
33    pub filter_rules: Option<Vec<FilterRule>>,
34
35    /// The display mode of the view.
36    pub display_mode: Option<DisplayMode>,
37
38    /// The fields to display in the view.
39    pub display_fields: Option<Vec<String>>,
40
41    /// The number of documents to show per page.
42    pub page_size: Option<u32>,
43
44    /// The user who owns the saved view.
45    #[dto(skip)]
46    pub owner: Option<crate::id::UserId>,
47
48    /// Permissions for the saved view.
49    #[dto(skip)]
50    #[serde(flatten)]
51    pub permissions: ItemPermissions,
52}
53
54/// The display mode of a saved view.
55#[derive(Debug, Clone, Deserialize, Serialize)]
56#[serde(rename_all = "camelCase")]
57pub enum DisplayMode {
58    Table,
59    SmallCards,
60    LargeCards,
61}
62
63/// A filter rule for a saved view.
64#[derive(Debug, Clone, Deserialize, Serialize)]
65pub struct FilterRule {
66    /// The type of filter rule.
67    #[serde(rename = "rule_type")]
68    pub rule: FilterRuleType,
69
70    /// The value associated with the filter rule.
71    pub value: Option<String>,
72}
73
74/// The type of a filter rule.
75#[derive(Debug, Clone, Copy, Display)]
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
130impl Serialize for FilterRuleType {
131    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
132    where
133        S: serde::Serializer,
134    {
135        let value = if let FilterRuleType::Unknown(unknown) = self {
136            *unknown
137        } else {
138            // SAFETY: FilterRuleType must be a valid 0..=47
139            unsafe { *(std::ptr::from_ref::<FilterRuleType>(self)).cast::<u8>() }
140        };
141
142        serializer.serialize_u8(value)
143    }
144}
145
146impl<'de> serde::Deserialize<'de> for FilterRuleType {
147    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148    where
149        D: serde::Deserializer<'de>,
150    {
151        let value = u8::deserialize(deserializer)?;
152
153        let enum_value = match value {
154            0..=47 => unsafe { std::mem::transmute::<u16, FilterRuleType>(u16::from(value)) },
155            _ => FilterRuleType::Unknown(value),
156        };
157
158        Ok(enum_value)
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn filter_rule_type_boundary_values() {
168        let zero: FilterRuleType = serde_json::from_str("0").unwrap();
169        assert!(matches!(zero, FilterRuleType::TitleContains));
170
171        let forty_seven: FilterRuleType = serde_json::from_str("47").unwrap();
172        assert!(matches!(forty_seven, FilterRuleType::MimeTypeIs));
173
174        let forty_seven: FilterRuleType = serde_json::from_str("48").unwrap();
175        assert!(matches!(forty_seven, FilterRuleType::Unknown(48)));
176    }
177
178    #[test]
179    fn filter_rule_type_unknown_value_roundtrip() {
180        let unknown: FilterRuleType = serde_json::from_str("200").unwrap();
181        assert!(matches!(unknown, FilterRuleType::Unknown(200)));
182
183        let serialized = serde_json::to_string(&unknown).unwrap();
184        assert_eq!(serialized, "200");
185    }
186
187    #[test]
188    fn filter_rule_roundtrip_with_value() {
189        let rule = FilterRule {
190            rule: FilterRuleType::FullTextSearch,
191            value: Some("created:[-3 month to now]".to_string()),
192        };
193
194        let json = serde_json::to_string(&rule).unwrap();
195        let deserialized: FilterRule = serde_json::from_str(&json).unwrap();
196
197        assert!(matches!(deserialized.rule, FilterRuleType::FullTextSearch));
198        assert_eq!(
199            deserialized.value,
200            Some("created:[-3 month to now]".to_string())
201        );
202    }
203}