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