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