conjure_object/resource_identifier/
mod.rs1#![warn(missing_docs, clippy::all)]
17
18use lazy_static::lazy_static;
19use regex::Regex;
20use serde::de::{self, Deserialize, Deserializer, Unexpected};
21use serde::ser::{Serialize, Serializer};
22use std::borrow::Borrow;
23use std::cmp::Ordering;
24use std::error::Error;
25use std::fmt;
26use std::hash::{Hash, Hasher};
27use std::str::FromStr;
28
29#[cfg(test)]
30mod test;
31
32const RID_CLASS: &str = "ri";
33const SEPARATOR: &str = ".";
34
35lazy_static! {
36 static ref PARSE_REGEX: Regex = Regex::new(
37 r"(?x)
38 ^
39 ri
40 \.
41 ([a-z][a-z0-9\-]*) #service
42 \.
43 ((?:[a-z0-9][a-z0-9\-]*)?) #instance
44 \.
45 ([a-z][a-z0-9\-]*) #type
46 \.
47 ([a-zA-Z0-9_\-\.]+) #locator
48 $
49 ",
50 )
51 .unwrap();
52}
53
54#[derive(Clone)]
68pub struct ResourceIdentifier {
69 rid: String,
70 service_end: usize,
71 instance_end: usize,
72 type_end: usize,
73}
74
75impl ResourceIdentifier {
76 #[inline]
80 pub fn new(s: &str) -> Result<ResourceIdentifier, ParseError> {
81 s.parse()
82 }
83
84 pub fn from_components(
86 service: &str,
87 instance: &str,
88 type_: &str,
89 locator: &str,
90 ) -> Result<ResourceIdentifier, ParseError> {
91 if service.contains('.') || instance.contains('.') || type_.contains('.') {
94 return Err(ParseError(()));
95 }
96
97 format!("ri.{}.{}.{}.{}", service, instance, type_, locator).parse()
98 }
99
100 #[inline]
102 pub fn service(&self) -> &str {
103 let start = RID_CLASS.len() + SEPARATOR.len();
104 &self.rid[start..self.service_end]
105 }
106
107 #[inline]
109 pub fn instance(&self) -> &str {
110 let start = self.service_end + SEPARATOR.len();
111 &self.rid[start..self.instance_end]
112 }
113
114 #[inline]
116 pub fn type_(&self) -> &str {
117 let start = self.instance_end + SEPARATOR.len();
118 &self.rid[start..self.type_end]
119 }
120
121 #[inline]
123 pub fn locator(&self) -> &str {
124 let start = self.type_end + SEPARATOR.len();
125 &self.rid[start..]
126 }
127
128 #[inline]
130 pub fn as_str(&self) -> &str {
131 &self.rid
132 }
133
134 #[inline]
136 pub fn into_string(self) -> String {
137 self.rid
138 }
139}
140
141impl FromStr for ResourceIdentifier {
142 type Err = ParseError;
143
144 fn from_str(s: &str) -> Result<ResourceIdentifier, ParseError> {
145 let captures = match PARSE_REGEX.captures(s) {
146 Some(captures) => captures,
147 None => return Err(ParseError(())),
148 };
149
150 Ok(ResourceIdentifier {
151 rid: s.to_string(),
152 service_end: captures.get(1).unwrap().end(),
153 instance_end: captures.get(2).unwrap().end(),
154 type_end: captures.get(3).unwrap().end(),
155 })
156 }
157}
158
159impl Serialize for ResourceIdentifier {
160 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
161 where
162 S: Serializer,
163 {
164 self.rid.serialize(s)
165 }
166}
167
168impl<'de> Deserialize<'de> for ResourceIdentifier {
169 fn deserialize<D>(d: D) -> Result<ResourceIdentifier, D::Error>
170 where
171 D: Deserializer<'de>,
172 {
173 let s = String::deserialize(d)?;
175 ResourceIdentifier::new(&s)
176 .map_err(|_| de::Error::invalid_value(Unexpected::Str(&s), &"a resource identifier"))
177 }
178}
179
180impl AsRef<str> for ResourceIdentifier {
181 #[inline]
182 fn as_ref(&self) -> &str {
183 &self.rid
184 }
185}
186
187impl Borrow<str> for ResourceIdentifier {
189 #[inline]
190 fn borrow(&self) -> &str {
191 &self.rid
192 }
193}
194
195impl fmt::Debug for ResourceIdentifier {
197 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
198 fmt::Debug::fmt(&self.rid, fmt)
199 }
200}
201
202impl fmt::Display for ResourceIdentifier {
203 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
204 fmt::Display::fmt(&self.rid, fmt)
205 }
206}
207
208impl PartialEq for ResourceIdentifier {
209 #[inline]
210 fn eq(&self, other: &ResourceIdentifier) -> bool {
211 self.rid == other.rid
212 }
213}
214
215impl Eq for ResourceIdentifier {}
216
217impl PartialOrd for ResourceIdentifier {
218 #[inline]
219 fn partial_cmp(&self, other: &ResourceIdentifier) -> Option<Ordering> {
220 Some(self.cmp(other))
221 }
222
223 #[inline]
224 fn gt(&self, other: &ResourceIdentifier) -> bool {
225 self.rid > other.rid
226 }
227
228 #[inline]
229 fn ge(&self, other: &ResourceIdentifier) -> bool {
230 self.rid >= other.rid
231 }
232
233 #[inline]
234 fn lt(&self, other: &ResourceIdentifier) -> bool {
235 self.rid < other.rid
236 }
237
238 #[inline]
239 fn le(&self, other: &ResourceIdentifier) -> bool {
240 self.rid <= other.rid
241 }
242}
243
244impl Ord for ResourceIdentifier {
245 #[inline]
246 fn cmp(&self, other: &ResourceIdentifier) -> Ordering {
247 self.rid.cmp(&other.rid)
248 }
249}
250
251impl Hash for ResourceIdentifier {
252 #[inline]
253 fn hash<H>(&self, hasher: &mut H)
254 where
255 H: Hasher,
256 {
257 self.rid.hash(hasher)
258 }
259}
260
261#[derive(Debug)]
263pub struct ParseError(());
264
265impl fmt::Display for ParseError {
266 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
267 fmt.write_str("invalid resource identifier")
268 }
269}
270
271impl Error for ParseError {}