Skip to main content

sonic/store/
item.rs

1// Sonic
2//
3// Fast, lightweight and schema-less search backend
4// Copyright: 2019, Valerian Saliou <valerian@valeriansaliou.name>
5// License: Mozilla Public License v2.0 (MPL v2.0)
6
7pub struct StoreItemBuilder;
8
9#[derive(PartialEq, Debug)]
10pub struct StoreItem<'a>(
11    pub StoreItemPart<'a>,
12    pub Option<StoreItemPart<'a>>,
13    pub Option<StoreItemPart<'a>>,
14);
15
16#[derive(Copy, Clone, PartialEq, Debug)]
17pub struct StoreItemPart<'a>(&'a str);
18
19// TODO: Change variant names
20#[allow(clippy::enum_variant_names)]
21#[derive(PartialEq, Debug)]
22pub enum StoreItemError {
23    InvalidCollection,
24    InvalidBucket,
25    InvalidObject,
26}
27
28const STORE_ITEM_PART_LEN_MIN: usize = 1;
29const STORE_ITEM_PART_LEN_MAX: usize = 128;
30
31impl<'a> StoreItemPart<'a> {
32    #[allow(clippy::should_implement_trait)]
33    pub fn from_str(part: &'a str) -> Result<Self, ()> {
34        let len = part.len();
35
36        if (STORE_ITEM_PART_LEN_MIN..=STORE_ITEM_PART_LEN_MAX).contains(&len) && part.is_ascii() {
37            Ok(StoreItemPart(part))
38        } else {
39            Err(())
40        }
41    }
42
43    pub fn as_str(&self) -> &'a str {
44        self.0
45    }
46}
47
48impl<'a> AsRef<str> for StoreItemPart<'a> {
49    fn as_ref(&self) -> &str {
50        self.0
51    }
52}
53
54impl StoreItemBuilder {
55    pub fn from_depth_1(collection: &str) -> Result<StoreItem<'_>, StoreItemError> {
56        // Validate & box collection
57        if let Ok(collection_item) = StoreItemPart::from_str(collection) {
58            Ok(StoreItem(collection_item, None, None))
59        } else {
60            Err(StoreItemError::InvalidCollection)
61        }
62    }
63
64    pub fn from_depth_2<'a>(
65        collection: &'a str,
66        bucket: &'a str,
67    ) -> Result<StoreItem<'a>, StoreItemError> {
68        // Validate & box collection + bucket
69        match (
70            StoreItemPart::from_str(collection),
71            StoreItemPart::from_str(bucket),
72        ) {
73            (Ok(collection_item), Ok(bucket_item)) => {
74                Ok(StoreItem(collection_item, Some(bucket_item), None))
75            }
76            (Err(_), _) => Err(StoreItemError::InvalidCollection),
77            (_, Err(_)) => Err(StoreItemError::InvalidBucket),
78        }
79    }
80
81    pub fn from_depth_3<'a>(
82        collection: &'a str,
83        bucket: &'a str,
84        object: &'a str,
85    ) -> Result<StoreItem<'a>, StoreItemError> {
86        // Validate & box collection + bucket + object
87        match (
88            StoreItemPart::from_str(collection),
89            StoreItemPart::from_str(bucket),
90            StoreItemPart::from_str(object),
91        ) {
92            (Ok(collection_item), Ok(bucket_item), Ok(object_item)) => Ok(StoreItem(
93                collection_item,
94                Some(bucket_item),
95                Some(object_item),
96            )),
97            (Err(_), _, _) => Err(StoreItemError::InvalidCollection),
98            (_, Err(_), _) => Err(StoreItemError::InvalidBucket),
99            (_, _, Err(_)) => Err(StoreItemError::InvalidObject),
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn it_builds_store_item_depth_1() {
110        assert_eq!(
111            StoreItemBuilder::from_depth_1("c:test:1"),
112            Ok(StoreItem(StoreItemPart("c:test:1"), None, None))
113        );
114        assert_eq!(
115            StoreItemBuilder::from_depth_1(""),
116            Err(StoreItemError::InvalidCollection)
117        );
118    }
119
120    #[test]
121    fn it_builds_store_item_depth_2() {
122        assert_eq!(
123            StoreItemBuilder::from_depth_2("c:test:2", "b:test:2"),
124            Ok(StoreItem(
125                StoreItemPart("c:test:2"),
126                Some(StoreItemPart("b:test:2")),
127                None
128            ))
129        );
130        assert_eq!(
131            StoreItemBuilder::from_depth_2("", "b:test:2"),
132            Err(StoreItemError::InvalidCollection)
133        );
134        assert_eq!(
135            StoreItemBuilder::from_depth_2("c:test:2", ""),
136            Err(StoreItemError::InvalidBucket)
137        );
138    }
139
140    #[test]
141    fn it_builds_store_item_depth_3() {
142        assert_eq!(
143            StoreItemBuilder::from_depth_3("c:test:3", "b:test:3", "o:test:3"),
144            Ok(StoreItem(
145                StoreItemPart("c:test:3"),
146                Some(StoreItemPart("b:test:3")),
147                Some(StoreItemPart("o:test:3"))
148            ))
149        );
150        assert_eq!(
151            StoreItemBuilder::from_depth_3("", "b:test:3", "o:test:3"),
152            Err(StoreItemError::InvalidCollection)
153        );
154        assert_eq!(
155            StoreItemBuilder::from_depth_3("c:test:3", "", "o:test:3"),
156            Err(StoreItemError::InvalidBucket)
157        );
158        assert_eq!(
159            StoreItemBuilder::from_depth_3("c:test:3", "b:test:3", ""),
160            Err(StoreItemError::InvalidObject)
161        );
162    }
163}