1pub 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#[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 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 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 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}