1use core::{fmt::Debug, str};
2use serde::{
3 de::{self, Deserialize, Deserializer, Visitor},
4 ser::{Serialize, Serializer},
5};
6use std::borrow::{Borrow, Cow};
7
8use crate::{Basic, Error, Result, Str, Type};
9
10#[derive(PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
35pub struct ObjectPath<'a>(Str<'a>);
36
37impl<'a> ObjectPath<'a> {
38 pub fn as_ref(&self) -> ObjectPath<'_> {
40 ObjectPath(self.0.as_ref())
41 }
42
43 pub fn as_str(&self) -> &str {
45 self.0.as_str()
46 }
47
48 pub fn as_bytes(&self) -> &[u8] {
50 self.0.as_bytes()
51 }
52
53 pub unsafe fn from_bytes_unchecked<'s: 'a>(bytes: &'s [u8]) -> Self {
62 Self(std::str::from_utf8_unchecked(bytes).into())
63 }
64
65 pub fn from_str_unchecked<'s: 'a>(path: &'s str) -> Self {
70 Self(path.into())
71 }
72
73 pub fn from_static_str(name: &'static str) -> Result<Self> {
75 validate(name.as_bytes())?;
76
77 Ok(Self::from_static_str_unchecked(name))
78 }
79
80 pub const fn from_static_str_unchecked(name: &'static str) -> Self {
82 Self(Str::from_static(name))
83 }
84
85 pub fn from_string_unchecked(path: String) -> Self {
90 Self(path.into())
91 }
92
93 pub fn len(&self) -> usize {
95 self.0.len()
96 }
97
98 pub fn is_empty(&self) -> bool {
100 self.0.is_empty()
101 }
102
103 pub fn to_owned(&self) -> ObjectPath<'static> {
105 ObjectPath(self.0.to_owned())
106 }
107
108 pub fn into_owned(self) -> ObjectPath<'static> {
110 ObjectPath(self.0.into_owned())
111 }
112}
113
114impl std::default::Default for ObjectPath<'_> {
115 fn default() -> Self {
116 ObjectPath::from_static_str_unchecked("/")
117 }
118}
119
120impl Basic for ObjectPath<'_> {
121 const SIGNATURE_CHAR: char = 'o';
122 const SIGNATURE_STR: &'static str = "o";
123}
124
125impl Type for ObjectPath<'_> {
126 const SIGNATURE: &'static crate::Signature = &crate::Signature::ObjectPath;
127}
128
129impl<'a> TryFrom<&'a [u8]> for ObjectPath<'a> {
130 type Error = Error;
131
132 fn try_from(value: &'a [u8]) -> Result<Self> {
133 validate(value)?;
134
135 unsafe { Ok(Self::from_bytes_unchecked(value)) }
137 }
138}
139
140impl<'a> TryFrom<&'a str> for ObjectPath<'a> {
142 type Error = Error;
143
144 fn try_from(value: &'a str) -> Result<Self> {
145 Self::try_from(value.as_bytes())
146 }
147}
148
149impl TryFrom<String> for ObjectPath<'_> {
150 type Error = Error;
151
152 fn try_from(value: String) -> Result<Self> {
153 validate(value.as_bytes())?;
154
155 Ok(Self::from_string_unchecked(value))
156 }
157}
158
159impl<'a> TryFrom<Cow<'a, str>> for ObjectPath<'a> {
160 type Error = Error;
161
162 fn try_from(value: Cow<'a, str>) -> Result<Self> {
163 match value {
164 Cow::Borrowed(s) => Self::try_from(s),
165 Cow::Owned(s) => Self::try_from(s),
166 }
167 }
168}
169
170impl<'o> From<&ObjectPath<'o>> for ObjectPath<'o> {
171 fn from(o: &ObjectPath<'o>) -> Self {
172 o.clone()
173 }
174}
175
176impl std::ops::Deref for ObjectPath<'_> {
177 type Target = str;
178
179 fn deref(&self) -> &Self::Target {
180 self.as_str()
181 }
182}
183
184impl PartialEq<str> for ObjectPath<'_> {
185 fn eq(&self, other: &str) -> bool {
186 self.as_str() == other
187 }
188}
189
190impl PartialEq<&str> for ObjectPath<'_> {
191 fn eq(&self, other: &&str) -> bool {
192 self.as_str() == *other
193 }
194}
195
196impl Debug for ObjectPath<'_> {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 f.debug_tuple("ObjectPath").field(&self.as_str()).finish()
199 }
200}
201
202impl std::fmt::Display for ObjectPath<'_> {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 std::fmt::Display::fmt(&self.as_str(), f)
205 }
206}
207
208impl Serialize for ObjectPath<'_> {
209 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
210 where
211 S: Serializer,
212 {
213 serializer.serialize_str(self.as_str())
214 }
215}
216
217impl<'de: 'a, 'a> Deserialize<'de> for ObjectPath<'a> {
218 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
219 where
220 D: Deserializer<'de>,
221 {
222 let visitor = ObjectPathVisitor;
223
224 deserializer.deserialize_str(visitor)
225 }
226}
227
228struct ObjectPathVisitor;
229
230impl<'de> Visitor<'de> for ObjectPathVisitor {
231 type Value = ObjectPath<'de>;
232
233 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 formatter.write_str("an ObjectPath")
235 }
236
237 #[inline]
238 fn visit_borrowed_str<E>(self, value: &'de str) -> core::result::Result<ObjectPath<'de>, E>
239 where
240 E: serde::de::Error,
241 {
242 ObjectPath::try_from(value).map_err(serde::de::Error::custom)
243 }
244}
245
246fn validate(path: &[u8]) -> Result<()> {
247 use winnow::{combinator::separated, stream::AsChar, token::take_while, Parser};
248 let allowed_chars = (AsChar::is_alphanum, b'_');
257 let name = take_while::<_, _, ()>(1.., allowed_chars);
258 let mut full_path = (b'/', separated(0.., name, b'/')).map(|_: (u8, ())| ());
259
260 full_path.parse(path).map_err(|_| Error::InvalidObjectPath)
261}
262
263#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, Type)]
265pub struct OwnedObjectPath(ObjectPath<'static>);
266
267impl OwnedObjectPath {
268 pub fn into_inner(self) -> ObjectPath<'static> {
269 self.0
270 }
271}
272
273impl Basic for OwnedObjectPath {
274 const SIGNATURE_CHAR: char = ObjectPath::SIGNATURE_CHAR;
275 const SIGNATURE_STR: &'static str = ObjectPath::SIGNATURE_STR;
276}
277
278impl std::ops::Deref for OwnedObjectPath {
279 type Target = ObjectPath<'static>;
280
281 fn deref(&self) -> &Self::Target {
282 &self.0
283 }
284}
285
286impl<'a> Borrow<ObjectPath<'a>> for OwnedObjectPath {
287 fn borrow(&self) -> &ObjectPath<'a> {
288 &self.0
289 }
290}
291
292impl std::convert::From<OwnedObjectPath> for ObjectPath<'static> {
293 fn from(o: OwnedObjectPath) -> Self {
294 o.into_inner()
295 }
296}
297
298impl std::convert::From<OwnedObjectPath> for crate::Value<'_> {
299 fn from(o: OwnedObjectPath) -> Self {
300 o.into_inner().into()
301 }
302}
303
304impl<'unowned, 'owned: 'unowned> From<&'owned OwnedObjectPath> for ObjectPath<'unowned> {
305 fn from(o: &'owned OwnedObjectPath) -> Self {
306 ObjectPath::from_str_unchecked(o.as_str())
307 }
308}
309
310impl<'a> std::convert::From<ObjectPath<'a>> for OwnedObjectPath {
311 fn from(o: ObjectPath<'a>) -> Self {
312 OwnedObjectPath(o.into_owned())
313 }
314}
315
316impl TryFrom<&'_ str> for OwnedObjectPath {
317 type Error = Error;
318
319 fn try_from(value: &str) -> Result<Self> {
320 Ok(Self::from(ObjectPath::try_from(value)?))
321 }
322}
323
324impl TryFrom<String> for OwnedObjectPath {
325 type Error = Error;
326
327 fn try_from(value: String) -> Result<Self> {
328 Ok(Self::from(ObjectPath::try_from(value)?))
329 }
330}
331
332impl<'de> Deserialize<'de> for OwnedObjectPath {
333 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
334 where
335 D: Deserializer<'de>,
336 {
337 String::deserialize(deserializer)
338 .and_then(|s| ObjectPath::try_from(s).map_err(|e| de::Error::custom(e.to_string())))
339 .map(Self)
340 }
341}
342
343impl std::fmt::Display for OwnedObjectPath {
344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 std::fmt::Display::fmt(&self.as_str(), f)
346 }
347}
348
349#[cfg(test)]
350mod unit {
351 use super::*;
352
353 #[test]
354 fn owned_from_reader() {
355 let json_str = "\"/some/path\"";
357 serde_json::de::from_reader::<_, OwnedObjectPath>(json_str.as_bytes()).unwrap();
358 }
359}