Skip to main content

cm_types/
lib.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A crate containing common Component Manager types used in Component Manifests
6//! (`.cml` files and binary `.cm` files). These types come with `serde` serialization
7//! and deserialization implementations that perform the required validation.
8
9use fidl_fuchsia_component_decl as fdecl;
10use fidl_fuchsia_io as fio;
11use flyweights::FlyStr;
12use serde::{Deserialize, Serialize, de, ser};
13use std::borrow::Borrow;
14use std::ffi::CString;
15use std::fmt::{self, Display};
16use std::hash::{Hash, Hasher};
17use std::ops::Deref;
18use std::path::PathBuf;
19use std::str::FromStr;
20use std::sync::LazyLock;
21use std::{cmp, iter};
22use thiserror::Error;
23
24/// A default base URL from which to parse relative component URL
25/// components.
26static DEFAULT_BASE_URL: LazyLock<url::Url> =
27    LazyLock::new(|| url::Url::parse("relative:///").unwrap());
28
29/// Generate `impl From` for two trivial enums with identical values, allowing
30/// converting to/from each other.
31/// This is useful if you have a FIDL-generated enum and a hand-rolled
32/// one that contain the same values.
33/// # Arguments
34///
35/// * `$a`, `$b` - The enums to generate `impl From` for. Order doesn't matter because
36///     implementation will be generated for both. Enums should be trivial.
37/// * `id` - Exhaustive list of all enum values.
38/// # Examples
39///
40/// ```
41/// mod a {
42///     #[derive(Debug, PartialEq, Eq)]
43///     pub enum Streetlight {
44///         Green,
45///         Yellow,
46///         Red,
47///     }
48/// }
49///
50/// mod b {
51///     #[derive(Debug, PartialEq, Eq)]
52///     pub enum Streetlight {
53///         Green,
54///         Yellow,
55///         Red,
56///     }
57/// }
58///
59/// symmetrical_enums!(a::Streetlight, b::Streetlight, Green, Yellow, Red);
60///
61/// assert_eq!(a::Streetlight::Green, b::Streetlight::Green.into());
62/// assert_eq!(b::Streetlight::Green, a::Streetlight::Green.into());
63/// ```
64#[macro_export]
65macro_rules! symmetrical_enums {
66    ($a:ty , $b:ty, $($id: ident),*) => {
67        impl From<$a> for $b {
68            fn from(input: $a) -> Self {
69                match input {
70                    $( <$a>::$id => <$b>::$id, )*
71                }
72            }
73        }
74
75        impl From<$b> for $a {
76            fn from(input: $b) -> Self {
77                match input {
78                    $( <$b>::$id => <$a>::$id, )*
79                }
80            }
81        }
82    };
83}
84
85/// The error representing a failure to parse a type from string.
86#[derive(Serialize, Clone, Deserialize, Debug, Error, PartialEq, Eq)]
87pub enum ParseError {
88    /// The string did not match a valid value.
89    #[error("invalid value")]
90    InvalidValue,
91    /// The string did not match a valid absolute or relative component URL
92    #[error("invalid URL: {details}")]
93    InvalidComponentUrl { details: String },
94    /// The string was empty.
95    #[error("empty")]
96    Empty,
97    /// The string was too long.
98    #[error("too long")]
99    TooLong,
100    /// A required leading slash was missing.
101    #[error("no leading slash")]
102    NoLeadingSlash,
103    /// The path segment is invalid.
104    #[error("invalid path segment")]
105    InvalidSegment,
106}
107
108pub const MAX_NAME_LENGTH: usize = name::MAX_NAME_LENGTH;
109pub const MAX_LONG_NAME_LENGTH: usize = 1024;
110pub const MAX_PATH_LENGTH: usize = fio::MAX_PATH_LENGTH as usize;
111pub const MAX_URL_LENGTH: usize = 4096;
112
113/// This asks for the maximum possible rights that the parent connection will allow; this will
114/// include the writable and executable rights if the parent connection has them, but won't fail if
115/// it doesn't.
116pub const FLAGS_MAX_POSSIBLE_RIGHTS: fio::Flags = fio::PERM_READABLE
117    .union(fio::Flags::PERM_INHERIT_WRITE)
118    .union(fio::Flags::PERM_INHERIT_EXECUTE);
119
120/// A name that can refer to a component, collection, or other entity in the
121/// Component Manifest. Its length is bounded to `MAX_NAME_LENGTH`.
122pub type Name = BoundedName<MAX_NAME_LENGTH>;
123/// A `Name` with a higher string capacity of `MAX_LONG_NAME_LENGTH`.
124pub type LongName = BoundedName<MAX_LONG_NAME_LENGTH>;
125
126/// A `BoundedName` is a `Name` that can have a max length of `N` bytes.
127#[derive(Serialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
128pub struct BoundedName<const N: usize>(FlyStr);
129
130impl Name {
131    #[inline]
132    pub fn to_long(self) -> LongName {
133        BoundedName(self.0)
134    }
135}
136
137impl LongName {
138    /// Converts this [`LongName`] into a [`Name`] without reallocating.
139    ///
140    /// # Panics
141    ///
142    /// Panics if the length of the name exceeds [`MAX_NAME_LENGTH`].
143    #[inline]
144    pub fn to_short_infallible(self) -> Name {
145        assert!(self.0.len() <= MAX_NAME_LENGTH, "long name does not fit in short name");
146        BoundedName(self.0)
147    }
148}
149
150impl<const N: usize> BoundedName<N> {
151    /// Creates a `BoundedName` from a `&str` slice, returning an `Err` if the string
152    /// fails validation. The string must be non-empty, no more than `N`
153    /// characters in length, and consist of one or more of the
154    /// following characters: `A-Z`, `a-z`, `0-9`, `_`, `.`, `-`. It may not start
155    /// with `.` or `-`.
156    pub fn new(s: impl AsRef<str>) -> Result<Self, ParseError> {
157        let s = s.as_ref();
158        validate_name::<N>(s)?;
159        Ok(Self(FlyStr::new(s)))
160    }
161
162    /// Private variant of [`BoundedName::new`] that does not perform correctness checks.
163    /// For efficiency when the caller is sure `s` is a valid [`BoundedName`].
164    fn new_unchecked<S: AsRef<str> + ?Sized>(s: &S) -> Self {
165        Self(FlyStr::new(s.as_ref()))
166    }
167
168    #[inline]
169    pub fn as_str(&self) -> &str {
170        &self.0
171    }
172
173    #[inline]
174    pub fn is_empty(&self) -> bool {
175        self.0.is_empty()
176    }
177
178    #[inline]
179    pub fn len(&self) -> usize {
180        self.0.len()
181    }
182}
183
184impl<const N: usize> AsRef<str> for BoundedName<N> {
185    #[inline]
186    fn as_ref(&self) -> &str {
187        self.as_str()
188    }
189}
190
191impl<const N: usize> AsRef<BoundedBorrowedName<N>> for BoundedName<N> {
192    #[inline]
193    fn as_ref(&self) -> &BoundedBorrowedName<N> {
194        BoundedBorrowedName::<N>::new_unchecked(self)
195    }
196}
197
198impl<const N: usize> AsRef<BoundedName<N>> for BoundedName<N> {
199    #[inline]
200    fn as_ref(&self) -> &BoundedName<N> {
201        self
202    }
203}
204
205impl<const N: usize> Borrow<str> for BoundedName<N> {
206    #[inline]
207    fn borrow(&self) -> &str {
208        &self.0
209    }
210}
211
212impl<const N: usize> Deref for BoundedName<N> {
213    type Target = BoundedBorrowedName<N>;
214
215    #[inline]
216    fn deref(&self) -> &BoundedBorrowedName<N> {
217        BoundedBorrowedName::new_unchecked(self.0.as_str())
218    }
219}
220
221impl<const N: usize> Borrow<BoundedBorrowedName<N>> for BoundedName<N> {
222    #[inline]
223    fn borrow(&self) -> &BoundedBorrowedName<N> {
224        self.deref()
225    }
226}
227
228impl<const N: usize> From<BoundedName<N>> for FlyStr {
229    #[inline]
230    fn from(o: BoundedName<N>) -> Self {
231        o.0
232    }
233}
234
235impl<'a, const N: usize> From<&'a BoundedName<N>> for &'a FlyStr {
236    #[inline]
237    fn from(o: &'a BoundedName<N>) -> Self {
238        &o.0
239    }
240}
241
242impl<const N: usize> PartialEq<&str> for BoundedName<N> {
243    #[inline]
244    fn eq(&self, o: &&str) -> bool {
245        &*self.0 == *o
246    }
247}
248
249impl<const N: usize> PartialEq<String> for BoundedName<N> {
250    #[inline]
251    fn eq(&self, o: &String) -> bool {
252        &*self.0 == *o
253    }
254}
255
256impl<const N: usize> PartialEq<BoundedBorrowedName<N>> for BoundedName<N> {
257    #[inline]
258    fn eq(&self, o: &BoundedBorrowedName<N>) -> bool {
259        &self.0 == &o.0
260    }
261}
262
263impl<const N: usize> Hash for BoundedName<N> {
264    #[inline]
265    fn hash<H: Hasher>(&self, state: &mut H) {
266        self.0.as_str().hash(state)
267    }
268}
269
270impl<const N: usize> fmt::Display for BoundedName<N> {
271    #[inline]
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        <FlyStr as fmt::Display>::fmt(&self.0, f)
274    }
275}
276
277impl<const N: usize> FromStr for BoundedName<N> {
278    type Err = ParseError;
279
280    #[inline]
281    fn from_str(name: &str) -> Result<Self, Self::Err> {
282        Self::new(name)
283    }
284}
285
286impl<const N: usize> From<&BoundedBorrowedName<N>> for BoundedName<N> {
287    #[inline]
288    fn from(o: &BoundedBorrowedName<N>) -> Self {
289        Self(o.0.into())
290    }
291}
292
293impl<const N: usize> From<BoundedName<N>> for String {
294    #[inline]
295    fn from(name: BoundedName<N>) -> String {
296        name.0.into()
297    }
298}
299
300impl From<Name> for LongName {
301    #[inline]
302    fn from(name: Name) -> Self {
303        Self(name.0)
304    }
305}
306
307/// Unowned variant of [`Name`]. [`Name`] for more details.
308pub type BorrowedName = BoundedBorrowedName<MAX_NAME_LENGTH>;
309/// Unowned variant of [`LongName`]. [`LongName`] for more details.
310pub type BorrowedLongName = BoundedBorrowedName<MAX_LONG_NAME_LENGTH>;
311
312/// Like [`BoundedName`], except it holds a string slice rather than an allocated string. For
313/// example, the [`Path`] API uses this to return path segments without making an allocation.
314#[derive(Serialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
315#[repr(transparent)]
316pub struct BoundedBorrowedName<const N: usize>(str);
317
318impl BorrowedName {
319    #[inline]
320    pub fn to_long(&self) -> &BorrowedLongName {
321        // SAFETY: `BorrowedName` and `BorrowedLongName` share the same representation.
322        // Furthermore, every `BorrowedName` is a valid `BorrowedLongName`. Therefore, this
323        // typecast is safe.
324        unsafe { &*(self as *const BorrowedName as *const BorrowedLongName) }
325    }
326}
327
328impl<const N: usize> BoundedBorrowedName<N> {
329    /// Creates a `BoundedBorrowedName` from a `&str` slice, which obeys the same
330    /// rules as `BoundedName`.
331    pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> Result<&Self, ParseError> {
332        validate_name::<N>(s.as_ref())?;
333        Ok(Self::new_unchecked(s))
334    }
335
336    /// Private variant of [`BoundedBorrowedName::new`] that does not perform correctness checks.
337    /// For efficiency when the caller is sure `s` is a valid [`BoundedName`].
338    fn new_unchecked<S: AsRef<str> + ?Sized>(s: &S) -> &Self {
339        // SAFETY: `&str` is the transparent representation of `BorrowedName`. This function is
340        // private, and it is only called from places that are certain the `&str` matches the
341        // `BorrowedName` requirements. Therefore, this typecast is safe.
342        unsafe { &*(s.as_ref() as *const str as *const Self) }
343    }
344
345    #[inline]
346    pub fn as_str(&self) -> &str {
347        &self.0
348    }
349
350    #[inline]
351    pub fn is_empty(&self) -> bool {
352        self.0.is_empty()
353    }
354
355    #[inline]
356    pub fn len(&self) -> usize {
357        self.0.len()
358    }
359}
360
361fn validate_name<const N: usize>(name: &str) -> Result<(), ParseError> {
362    if name.is_empty() {
363        return Err(ParseError::Empty);
364    }
365    if name.len() > N {
366        return Err(ParseError::TooLong);
367    }
368    let mut char_iter = name.chars();
369    let first_char = char_iter.next().unwrap();
370    if !first_char.is_ascii_alphanumeric() && first_char != '_' {
371        return Err(ParseError::InvalidValue);
372    }
373    let valid_fn = |c: char| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.';
374    if !char_iter.all(valid_fn) {
375        return Err(ParseError::InvalidValue);
376    }
377    Ok(())
378}
379
380impl<const N: usize> ToOwned for BoundedBorrowedName<N> {
381    type Owned = BoundedName<N>;
382
383    fn to_owned(&self) -> Self::Owned {
384        BoundedName::<N>::new_unchecked(&self.0)
385    }
386}
387
388impl<const N: usize> AsRef<str> for BoundedBorrowedName<N> {
389    #[inline]
390    fn as_ref(&self) -> &str {
391        &self.0
392    }
393}
394
395impl<const N: usize> Borrow<str> for BoundedBorrowedName<N> {
396    #[inline]
397    fn borrow(&self) -> &str {
398        &self.0
399    }
400}
401
402impl<const N: usize> Borrow<str> for &BoundedBorrowedName<N> {
403    #[inline]
404    fn borrow(&self) -> &str {
405        &self.0
406    }
407}
408
409impl<'a, const N: usize> From<&'a BoundedBorrowedName<N>> for &'a str {
410    #[inline]
411    fn from(o: &'a BoundedBorrowedName<N>) -> Self {
412        &o.0
413    }
414}
415
416impl<const N: usize> PartialEq<&str> for BoundedBorrowedName<N> {
417    #[inline]
418    fn eq(&self, o: &&str) -> bool {
419        &self.0 == *o
420    }
421}
422
423impl<const N: usize> PartialEq<String> for BoundedBorrowedName<N> {
424    #[inline]
425    fn eq(&self, o: &String) -> bool {
426        &self.0 == &*o
427    }
428}
429
430impl<const N: usize> PartialEq<BoundedName<N>> for BoundedBorrowedName<N> {
431    #[inline]
432    fn eq(&self, o: &BoundedName<N>) -> bool {
433        self.0 == *o.0
434    }
435}
436
437impl<const N: usize> Hash for BoundedBorrowedName<N> {
438    #[inline]
439    fn hash<H: Hasher>(&self, state: &mut H) {
440        self.0.hash(state)
441    }
442}
443
444impl<const N: usize> fmt::Display for BoundedBorrowedName<N> {
445    #[inline]
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        <str as fmt::Display>::fmt(&self.0, f)
448    }
449}
450
451impl<'a> From<&'a BorrowedName> for &'a BorrowedLongName {
452    #[inline]
453    fn from(name: &'a BorrowedName) -> Self {
454        name.to_long()
455    }
456}
457
458impl<'de, const N: usize> de::Deserialize<'de> for BoundedName<N> {
459    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
460    where
461        D: de::Deserializer<'de>,
462    {
463        struct Visitor<const N: usize>;
464
465        impl<'de, const N: usize> de::Visitor<'de> for Visitor<N> {
466            type Value = BoundedName<{ N }>;
467
468            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469                f.write_str(&format!(
470                    "a non-empty string no more than {} characters in length, \
471                    consisting of [A-Za-z0-9_.-] and starting with [A-Za-z0-9_]",
472                    N
473                ))
474            }
475
476            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
477            where
478                E: de::Error,
479            {
480                s.parse().map_err(|err| match err {
481                    ParseError::InvalidValue => E::invalid_value(
482                        de::Unexpected::Str(s),
483                        &"a name that consists of [A-Za-z0-9_.-] and starts with [A-Za-z0-9_]",
484                    ),
485                    ParseError::TooLong | ParseError::Empty => E::invalid_length(
486                        s.len(),
487                        &format!("a non-empty name no more than {} characters in length", N)
488                            .as_str(),
489                    ),
490                    e => {
491                        panic!("unexpected parse error: {:?}", e);
492                    }
493                })
494            }
495        }
496        deserializer.deserialize_string(Visitor)
497    }
498}
499
500impl IterablePath for Name {
501    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
502        iter::once(self as &BorrowedName)
503    }
504}
505
506impl IterablePath for &Name {
507    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
508        iter::once(*self as &BorrowedName)
509    }
510}
511
512/// [NamespacePath] is the same as [Path] but accepts `"/"` (which is also a valid namespace
513/// path).
514///
515/// Note that while `"/"` is accepted, `"."` (which is synonymous in fuchsia.io) is rejected.
516#[derive(Eq, Ord, PartialOrd, PartialEq, Hash, Clone)]
517pub struct NamespacePath(RelativePath);
518
519impl NamespacePath {
520    /// Like [Path::new] but `path` may be `/`.
521    pub fn new(path: impl AsRef<str>) -> Result<Self, ParseError> {
522        let path = path.as_ref();
523        if path.is_empty() {
524            return Err(ParseError::Empty);
525        }
526        if path == "." {
527            return Err(ParseError::InvalidValue);
528        }
529        if !path.starts_with('/') {
530            return Err(ParseError::NoLeadingSlash);
531        }
532        if path.len() > MAX_PATH_LENGTH {
533            return Err(ParseError::TooLong);
534        }
535        if path == "/" {
536            Ok(Self(RelativePath::dot()))
537        } else {
538            let path: RelativePath = path[1..].parse()?;
539            if path.is_dot() {
540                // "/." is not a valid NamespacePath
541                return Err(ParseError::InvalidSegment);
542            }
543            Ok(Self(path))
544        }
545    }
546
547    /// Returns the [NamespacePath] for `"/"`.
548    pub fn root() -> Self {
549        Self(RelativePath::dot())
550    }
551
552    pub fn is_root(&self) -> bool {
553        self.0.is_dot()
554    }
555
556    /// Splits the path according to `"/"`.
557    pub fn split(&self) -> Vec<&BorrowedName> {
558        self.0.split()
559    }
560
561    pub fn to_path_buf(&self) -> PathBuf {
562        PathBuf::from(self.to_string())
563    }
564
565    /// Returns a path that represents the parent directory of this one, or None if this is a
566    /// root dir.
567    pub fn parent(&self) -> Option<Self> {
568        self.0.parent().map(|p| Self(p))
569    }
570
571    /// Returns whether `prefix` is a prefix of `self` in terms of path segments.
572    ///
573    /// For example:
574    /// ```
575    /// Path("/pkg/data").has_prefix("/pkg") == true
576    /// Path("/pkg_data").has_prefix("/pkg") == false
577    /// ```
578    pub fn has_prefix(&self, prefix: &Self) -> bool {
579        let my_segments = self.split();
580        let prefix_segments = prefix.split();
581        if prefix_segments.len() > my_segments.len() {
582            return false;
583        }
584        prefix_segments.into_iter().zip(my_segments.into_iter()).all(|(a, b)| a == b)
585    }
586
587    /// The last path segment, or None.
588    pub fn basename(&self) -> Option<&BorrowedName> {
589        self.0.basename()
590    }
591
592    pub fn pop_front(&mut self) -> Option<Name> {
593        self.0.pop_front()
594    }
595
596    pub fn into_relative(self) -> RelativePath {
597        self.0
598    }
599}
600
601impl IterablePath for NamespacePath {
602    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
603        self.0.iter_segments()
604    }
605}
606
607impl serde::ser::Serialize for NamespacePath {
608    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
609    where
610        S: serde::ser::Serializer,
611    {
612        self.to_string().serialize(serializer)
613    }
614}
615
616impl TryFrom<CString> for NamespacePath {
617    type Error = ParseError;
618
619    fn try_from(path: CString) -> Result<Self, ParseError> {
620        Self::new(path.into_string().map_err(|_| ParseError::InvalidValue)?)
621    }
622}
623
624impl From<NamespacePath> for CString {
625    fn from(path: NamespacePath) -> Self {
626        CString::new(path.to_string()).expect("validated path contains no NUL")
627    }
628}
629
630impl From<NamespacePath> for String {
631    fn from(path: NamespacePath) -> Self {
632        path.to_string()
633    }
634}
635
636impl FromStr for NamespacePath {
637    type Err = ParseError;
638
639    fn from_str(path: &str) -> Result<Self, Self::Err> {
640        Self::new(path)
641    }
642}
643
644impl fmt::Debug for NamespacePath {
645    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
646        write!(f, "{}", self)
647    }
648}
649
650impl fmt::Display for NamespacePath {
651    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
652        if !self.0.is_dot() { write!(f, "/{}", self.0) } else { write!(f, "/") }
653    }
654}
655
656/// A path type used throughout Component Framework, along with its variants [NamespacePath] and
657/// [RelativePath]. Examples of use:
658///
659/// - [NamespacePath]: Namespace paths
660/// - [Path]: Outgoing paths and namespace paths that can't be "/"
661/// - [RelativePath]: Dictionary paths
662///
663/// [Path] obeys the following constraints:
664///
665/// - Is a [fuchsia.io.Path](https://fuchsia.dev/reference/fidl/fuchsia.io#Directory.Open).
666/// - Begins with `/`.
667/// - Is not `.`.
668/// - Contains at least one path segment (just `/` is disallowed).
669/// - Each path segment is a [Name]. (This is strictly more constrained than a fuchsia.io
670///   path segment.)
671#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
672pub struct Path(RelativePath);
673
674impl fmt::Debug for Path {
675    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
676        write!(f, "{}", self)
677    }
678}
679
680impl fmt::Display for Path {
681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682        write!(f, "/{}", self.0)
683    }
684}
685
686impl ser::Serialize for Path {
687    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
688    where
689        S: serde::ser::Serializer,
690    {
691        self.to_string().serialize(serializer)
692    }
693}
694
695impl Path {
696    /// Creates a [`Path`] from a [`String`], returning an `Err` if the string fails validation.
697    /// The string must be non-empty, no more than [`MAX_PATH_LENGTH`] bytes in length, start with
698    /// a leading `/`, not be exactly `/` or `.`, and each segment must be a valid [`Name`]. As a
699    /// result, [`Path`]s are always valid [`NamespacePath`]s.
700    pub fn new(path: impl AsRef<str>) -> Result<Self, ParseError> {
701        let path = path.as_ref();
702        if path.is_empty() {
703            return Err(ParseError::Empty);
704        }
705        if path == "/" || path == "." {
706            return Err(ParseError::InvalidValue);
707        }
708        if !path.starts_with('/') {
709            return Err(ParseError::NoLeadingSlash);
710        }
711        if path.len() > MAX_PATH_LENGTH {
712            return Err(ParseError::TooLong);
713        }
714        let path: RelativePath = path[1..].parse()?;
715        if path.is_dot() {
716            // "/." is not a valid Path
717            return Err(ParseError::InvalidSegment);
718        }
719        Ok(Self(path))
720    }
721
722    /// Splits the path according to "/".
723    pub fn split(&self) -> Vec<&BorrowedName> {
724        self.0.split()
725    }
726
727    pub fn to_path_buf(&self) -> PathBuf {
728        PathBuf::from(self.to_string())
729    }
730
731    /// Returns a path that represents the parent directory of this one. Returns [NamespacePath]
732    /// instead of [Path] because the parent could be the root dir.
733    pub fn parent(&self) -> NamespacePath {
734        let p = self.0.parent().expect("can't be root");
735        NamespacePath(p)
736    }
737
738    pub fn basename(&self) -> &BorrowedName {
739        self.0.basename().expect("can't be root")
740    }
741
742    // Attaches the path `other` to the end of `self`. Returns `true` on success, and false
743    // if the resulting path's length would exceed `MAX_PATH_LENGTH`.
744    #[must_use]
745    pub fn extend(&mut self, other: RelativePath) -> bool {
746        let rep: FlyStr = if !other.is_dot() {
747            format!("{}/{}", self.0.rep, other.rep).into()
748        } else {
749            // Nothing to do.
750            return true;
751        };
752        // Account for leading /
753        if rep.len() > MAX_PATH_LENGTH - 1 {
754            return false;
755        }
756        self.0.rep = rep;
757        true
758    }
759
760    // Attaches `segment` to the end of `self`. Returns `true` on success, and false
761    // if the resulting path's length would exceed `MAX_PATH_LENGTH`.
762    #[must_use]
763    pub fn push(&mut self, segment: Name) -> bool {
764        let rep: FlyStr = format!("{}/{}", self.0.rep, segment).into();
765        // Account for leading /
766        if rep.len() > MAX_PATH_LENGTH - 1 {
767            return false;
768        }
769        self.0.rep = rep;
770        true
771    }
772}
773
774impl IterablePath for Path {
775    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
776        Box::new(self.0.iter_segments())
777    }
778}
779
780impl From<Path> for NamespacePath {
781    fn from(value: Path) -> Self {
782        Self(value.0)
783    }
784}
785
786impl FromStr for Path {
787    type Err = ParseError;
788
789    fn from_str(path: &str) -> Result<Self, Self::Err> {
790        Self::new(path)
791    }
792}
793
794impl TryFrom<CString> for Path {
795    type Error = ParseError;
796
797    fn try_from(path: CString) -> Result<Self, ParseError> {
798        Self::new(path.into_string().map_err(|_| ParseError::InvalidValue)?)
799    }
800}
801
802impl From<Path> for CString {
803    fn from(path: Path) -> Self {
804        CString::new(path.to_string()).expect("validated path contains no NUL")
805    }
806}
807
808impl From<Path> for String {
809    fn from(path: Path) -> String {
810        path.to_string()
811    }
812}
813
814impl<'de> de::Deserialize<'de> for Path {
815    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
816    where
817        D: de::Deserializer<'de>,
818    {
819        struct Visitor;
820
821        impl<'de> de::Visitor<'de> for Visitor {
822            type Value = Path;
823
824            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
825                f.write_str(
826                    "a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH characters \
827                     in length, with a leading `/`, and containing no \
828                     empty path segments",
829                )
830            }
831
832            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
833            where
834                E: de::Error,
835            {
836                s.parse().map_err(|err| match err {
837                    ParseError::InvalidValue
838                    | ParseError::InvalidSegment
839                    | ParseError::NoLeadingSlash => E::invalid_value(
840                        de::Unexpected::Str(s),
841                        &"a path with leading `/` and non-empty segments, where each segment is no \
842                        more than fuchsia.io/MAX_NAME_LENGTH bytes in length, cannot be . or .., \
843                        and cannot contain embedded NULs",
844                    ),
845                    ParseError::TooLong | ParseError::Empty => E::invalid_length(
846                        s.len(),
847                        &"a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH bytes \
848                        in length",
849                    ),
850                    e => {
851                        panic!("unexpected parse error: {:?}", e);
852                    }
853                })
854            }
855        }
856        deserializer.deserialize_string(Visitor)
857    }
858}
859
860/// Same as [Path] except the path does not begin with `/`.
861#[derive(Eq, Ord, PartialOrd, PartialEq, Hash, Clone)]
862pub struct RelativePath {
863    rep: FlyStr,
864}
865
866impl RelativePath {
867    /// Like [Path::new] but `path` must not begin with `/` and may be `.`.
868    pub fn new(path: impl AsRef<str>) -> Result<Self, ParseError> {
869        let path: &str = path.as_ref();
870        if path == "." {
871            return Ok(Self::dot());
872        }
873        if path.is_empty() {
874            return Err(ParseError::Empty);
875        }
876        if path.len() > MAX_PATH_LENGTH {
877            return Err(ParseError::TooLong);
878        }
879        path.split('/').try_for_each(|s| {
880            Name::new(s).map(|_| ()).map_err(|e| match e {
881                ParseError::Empty => ParseError::InvalidValue,
882                _ => ParseError::InvalidSegment,
883            })
884        })?;
885        Ok(Self { rep: path.into() })
886    }
887
888    pub fn dot() -> Self {
889        Self { rep: ".".into() }
890    }
891
892    pub fn is_dot(&self) -> bool {
893        self.rep == "."
894    }
895
896    pub fn parent(&self) -> Option<Self> {
897        if self.is_dot() {
898            None
899        } else {
900            match self.rep.rfind('/') {
901                Some(idx) => Some(Self::new(&self.rep[0..idx]).unwrap()),
902                None => Some(Self::dot()),
903            }
904        }
905    }
906
907    pub fn split(&self) -> Vec<&BorrowedName> {
908        if self.is_dot() {
909            vec![]
910        } else {
911            self.rep.split('/').map(|s| BorrowedName::new_unchecked(s)).collect()
912        }
913    }
914
915    pub fn len(&self) -> usize {
916        if self.is_dot() { 0 } else { self.rep.split('/').count() }
917    }
918
919    pub fn basename(&self) -> Option<&BorrowedName> {
920        if self.is_dot() {
921            None
922        } else {
923            match self.rep.rfind('/') {
924                Some(idx) => Some(BorrowedName::new_unchecked(&self.rep[idx + 1..])),
925                None => Some(BorrowedName::new_unchecked(&self.rep)),
926            }
927        }
928    }
929
930    pub fn to_path_buf(&self) -> PathBuf {
931        if self.is_dot() { PathBuf::new() } else { PathBuf::from(self.to_string()) }
932    }
933
934    // Attaches the path `other` to the end of `self`. Returns `true` on success, and false
935    // if the resulting path's length would exceed `MAX_PATH_LENGTH`.
936    #[must_use]
937    pub fn extend(&mut self, other: Self) -> bool {
938        let rep = if self.is_dot() {
939            other.rep
940        } else if !other.is_dot() {
941            format!("{}/{}", self.rep, other.rep).into()
942        } else {
943            // Nothing to do.
944            return true;
945        };
946        if rep.len() > MAX_PATH_LENGTH {
947            return false;
948        }
949        self.rep = rep;
950        true
951    }
952
953    // Attaches `segment` to the end of `self`. Returns `true` on success, and false
954    // if the resulting path's length would exceed `MAX_PATH_LENGTH`.
955    #[must_use]
956    pub fn push(&mut self, segment: Name) -> bool {
957        let rep: FlyStr = if self.is_dot() {
958            format!("{segment}").into()
959        } else {
960            format!("{}/{}", self.rep, segment).into()
961        };
962        if rep.len() > MAX_PATH_LENGTH {
963            return false;
964        }
965        self.rep = rep;
966        true
967    }
968
969    pub fn pop_front(&mut self) -> Option<Name> {
970        if self.is_dot() {
971            None
972        } else {
973            let (rep, front) = match self.rep.find('/') {
974                Some(idx) => {
975                    let rep = self.rep[idx + 1..].into();
976                    let front = Name::new_unchecked(&self.rep[0..idx]);
977                    (rep, front)
978                }
979                None => (".".into(), Name::new_unchecked(&self.rep)),
980            };
981            self.rep = rep;
982            Some(front)
983        }
984    }
985}
986
987impl Default for RelativePath {
988    fn default() -> Self {
989        Self::dot()
990    }
991}
992
993impl IterablePath for RelativePath {
994    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
995        Box::new(self.split().into_iter())
996    }
997}
998
999impl FromStr for RelativePath {
1000    type Err = ParseError;
1001
1002    fn from_str(path: &str) -> Result<Self, Self::Err> {
1003        Self::new(path)
1004    }
1005}
1006
1007impl From<RelativePath> for String {
1008    fn from(path: RelativePath) -> String {
1009        path.to_string()
1010    }
1011}
1012
1013impl From<Vec<Name>> for RelativePath {
1014    fn from(segments: Vec<Name>) -> Self {
1015        if segments.is_empty() {
1016            Self::dot()
1017        } else {
1018            Self { rep: segments.iter().map(|s| s.as_str()).collect::<Vec<_>>().join("/").into() }
1019        }
1020    }
1021}
1022
1023impl From<Vec<&BorrowedName>> for RelativePath {
1024    fn from(segments: Vec<&BorrowedName>) -> Self {
1025        if segments.is_empty() {
1026            Self::dot()
1027        } else {
1028            Self {
1029                rep: segments.into_iter().map(|s| s.as_str()).collect::<Vec<_>>().join("/").into(),
1030            }
1031        }
1032    }
1033}
1034
1035impl fmt::Debug for RelativePath {
1036    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1037        write!(f, "{}", self)
1038    }
1039}
1040
1041impl fmt::Display for RelativePath {
1042    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1043        write!(f, "{}", self.rep)
1044    }
1045}
1046
1047impl ser::Serialize for RelativePath {
1048    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1049    where
1050        S: serde::ser::Serializer,
1051    {
1052        self.to_string().serialize(serializer)
1053    }
1054}
1055
1056impl<'de> de::Deserialize<'de> for RelativePath {
1057    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1058    where
1059        D: de::Deserializer<'de>,
1060    {
1061        struct Visitor;
1062
1063        impl<'de> de::Visitor<'de> for Visitor {
1064            type Value = RelativePath;
1065
1066            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1067                f.write_str(
1068                    "a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH characters \
1069                     in length, not starting with `/`, and containing no empty path segments",
1070                )
1071            }
1072
1073            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1074            where
1075                E: de::Error,
1076            {
1077                s.parse().map_err(|err| match err {
1078                    ParseError::InvalidValue
1079                    | ParseError::InvalidSegment
1080                    | ParseError::NoLeadingSlash => E::invalid_value(
1081                        de::Unexpected::Str(s),
1082                        &"a path with no leading `/` and non-empty segments",
1083                    ),
1084                    ParseError::TooLong | ParseError::Empty => E::invalid_length(
1085                        s.len(),
1086                        &"a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH characters \
1087                        in length",
1088                    ),
1089                    e => {
1090                        panic!("unexpected parse error: {:?}", e);
1091                    }
1092                })
1093            }
1094        }
1095        deserializer.deserialize_string(Visitor)
1096    }
1097}
1098
1099/// Path that separates the dirname and basename as different variables
1100/// (referencing type). Convenient for / path representations that split the
1101/// dirname and basename, like Fuchsia component decl.
1102#[derive(Debug, Clone, PartialEq, Eq)]
1103pub struct BorrowedSeparatedPath<'a> {
1104    pub dirname: &'a RelativePath,
1105    pub basename: &'a Name,
1106}
1107
1108impl BorrowedSeparatedPath<'_> {
1109    /// Converts this [BorrowedSeparatedPath] to the owned type.
1110    pub fn to_owned(&self) -> SeparatedPath {
1111        SeparatedPath { dirname: self.dirname.clone(), basename: self.basename.clone() }
1112    }
1113}
1114
1115impl fmt::Display for BorrowedSeparatedPath<'_> {
1116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1117        if !self.dirname.is_dot() {
1118            write!(f, "{}/{}", self.dirname, self.basename)
1119        } else {
1120            write!(f, "{}", self.basename)
1121        }
1122    }
1123}
1124
1125impl IterablePath for BorrowedSeparatedPath<'_> {
1126    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
1127        Box::new(self.dirname.iter_segments().chain(iter::once(self.basename as &BorrowedName)))
1128    }
1129}
1130
1131/// Path that separates the dirname and basename as different variables (owned
1132/// type). Convenient for path representations that split the dirname and
1133/// basename, like Fuchsia component decl.
1134#[derive(Debug, Clone, PartialEq, Eq)]
1135pub struct SeparatedPath {
1136    pub dirname: RelativePath,
1137    pub basename: Name,
1138}
1139
1140impl SeparatedPath {
1141    /// Obtains a reference to this [SeparatedPath] as the borrowed type.
1142    pub fn as_ref(&self) -> BorrowedSeparatedPath<'_> {
1143        BorrowedSeparatedPath { dirname: &self.dirname, basename: &self.basename }
1144    }
1145}
1146
1147impl IterablePath for SeparatedPath {
1148    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
1149        Box::new(self.dirname.iter_segments().chain(iter::once(&self.basename as &BorrowedName)))
1150    }
1151}
1152
1153impl fmt::Display for SeparatedPath {
1154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1155        if !self.dirname.is_dot() {
1156            write!(f, "{}/{}", self.dirname, self.basename)
1157        } else {
1158            write!(f, "{}", self.basename)
1159        }
1160    }
1161}
1162
1163/// Trait implemented by path types that provides an API to iterate over path segments.
1164pub trait IterablePath: Clone + Send + Sync {
1165    /// Returns a double-sided iterator over the segments in this path.
1166    fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send;
1167}
1168
1169/// A component URL. The URL is validated, but represented as a string to avoid
1170/// normalization and retain the original representation.
1171#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1172pub struct Url(FlyStr);
1173
1174impl Url {
1175    /// Creates a `Url` from a `&str` slice, returning an `Err` if the string fails
1176    /// validation. The string must be non-empty, no more than 4096 characters
1177    /// in length, and be a valid URL. See the [`url`](../../url/index.html) crate.
1178    pub fn new(url: impl AsRef<str> + Into<String>) -> Result<Self, ParseError> {
1179        Self::validate(url.as_ref())?;
1180        Ok(Self(FlyStr::new(url)))
1181    }
1182
1183    /// Verifies the given string is a valid absolute or relative component URL.
1184    pub fn validate(url_str: &str) -> Result<(), ParseError> {
1185        if url_str.is_empty() {
1186            return Err(ParseError::Empty);
1187        }
1188        if url_str.len() > MAX_URL_LENGTH {
1189            return Err(ParseError::TooLong);
1190        }
1191        match url::Url::parse(url_str).map(|url| (url, false)).or_else(|err| {
1192            if err == url::ParseError::RelativeUrlWithoutBase {
1193                DEFAULT_BASE_URL.join(url_str).map(|url| (url, true))
1194            } else {
1195                Err(err)
1196            }
1197        }) {
1198            Ok((url, is_relative)) => {
1199                let mut path = url.path();
1200                if path.starts_with('/') {
1201                    path = &path[1..];
1202                }
1203                if is_relative && url.fragment().is_none() {
1204                    // TODO(https://fxbug.dev/42070831): Fragments should be optional
1205                    // for relative path URLs.
1206                    //
1207                    // Historically, a component URL string without a scheme
1208                    // was considered invalid, unless it was only a fragment.
1209                    // Subpackages allow a relative path URL, and by current
1210                    // definition they require a fragment. By declaring a
1211                    // relative path without a fragment "invalid", we can avoid
1212                    // breaking tests that expect a path-only string to be
1213                    // invalid. Sadly this appears to be a behavior of the
1214                    // public API.
1215                    return Err(ParseError::InvalidComponentUrl {
1216                        details: "Relative URL has no resource fragment.".to_string(),
1217                    });
1218                }
1219                if url.host_str().unwrap_or("").is_empty()
1220                    && path.is_empty()
1221                    && url.fragment().is_none()
1222                {
1223                    return Err(ParseError::InvalidComponentUrl {
1224                        details: "URL is missing either `host`, `path`, and/or `resource`."
1225                            .to_string(),
1226                    });
1227                }
1228            }
1229            Err(err) => {
1230                return Err(ParseError::InvalidComponentUrl {
1231                    details: format!("Malformed URL: {err:?}."),
1232                });
1233            }
1234        }
1235        // Use the unparsed URL string so that the original format is preserved.
1236        Ok(())
1237    }
1238
1239    pub fn is_relative(&self) -> bool {
1240        matches!(url::Url::parse(&self.0), Err(url::ParseError::RelativeUrlWithoutBase))
1241    }
1242
1243    pub fn scheme(&self) -> Option<String> {
1244        url::Url::parse(&self.0).ok().map(|u| u.scheme().into())
1245    }
1246
1247    pub fn resource(&self) -> Option<String> {
1248        url::Url::parse(&self.0).ok().map(|u| u.fragment().map(str::to_string)).flatten()
1249    }
1250
1251    pub fn as_str(&self) -> &str {
1252        &*self.0
1253    }
1254}
1255
1256impl FromStr for Url {
1257    type Err = ParseError;
1258
1259    fn from_str(url: &str) -> Result<Self, Self::Err> {
1260        Self::new(url)
1261    }
1262}
1263
1264impl From<Url> for String {
1265    fn from(url: Url) -> String {
1266        url.0.into()
1267    }
1268}
1269
1270impl fmt::Display for Url {
1271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1272        fmt::Display::fmt(&self.0, f)
1273    }
1274}
1275
1276impl ser::Serialize for Url {
1277    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1278    where
1279        S: ser::Serializer,
1280    {
1281        self.to_string().serialize(serializer)
1282    }
1283}
1284
1285impl<'de> de::Deserialize<'de> for Url {
1286    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1287    where
1288        D: de::Deserializer<'de>,
1289    {
1290        struct Visitor;
1291
1292        impl<'de> de::Visitor<'de> for Visitor {
1293            type Value = Url;
1294
1295            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1296                f.write_str("a non-empty URL no more than 4096 characters in length")
1297            }
1298
1299            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1300            where
1301                E: de::Error,
1302            {
1303                s.parse().map_err(|err| match err {
1304                    ParseError::InvalidComponentUrl { details: _ } => {
1305                        E::invalid_value(de::Unexpected::Str(s), &"a valid URL")
1306                    }
1307                    ParseError::TooLong | ParseError::Empty => E::invalid_length(
1308                        s.len(),
1309                        &"a non-empty URL no more than 4096 characters in length",
1310                    ),
1311                    e => {
1312                        panic!("unexpected parse error: {:?}", e);
1313                    }
1314                })
1315            }
1316        }
1317        deserializer.deserialize_string(Visitor)
1318    }
1319}
1320
1321impl PartialEq<&str> for Url {
1322    fn eq(&self, o: &&str) -> bool {
1323        &*self.0 == *o
1324    }
1325}
1326
1327impl PartialEq<String> for Url {
1328    fn eq(&self, o: &String) -> bool {
1329        &*self.0 == *o
1330    }
1331}
1332
1333/// A URL scheme.
1334#[derive(Serialize, Clone, Debug, Eq, Hash, PartialEq)]
1335pub struct UrlScheme(FlyStr);
1336
1337impl UrlScheme {
1338    /// Creates a `UrlScheme` from a `String`, returning an `Err` if the string fails
1339    /// validation. The string must be non-empty and no more than 100 characters
1340    /// in length. It must start with a lowercase ASCII letter (a-z),
1341    /// and contain only lowercase ASCII letters, digits, `+`, `-`, and `.`.
1342    pub fn new(url_scheme: impl AsRef<str> + Into<String>) -> Result<Self, ParseError> {
1343        Self::validate(url_scheme.as_ref())?;
1344        Ok(UrlScheme(FlyStr::new(url_scheme)))
1345    }
1346
1347    /// Validates `url_scheme` but does not construct a new `UrlScheme` object.
1348    /// See [`UrlScheme::new`] for validation details.
1349    pub fn validate(url_scheme: &str) -> Result<(), ParseError> {
1350        if url_scheme.is_empty() {
1351            return Err(ParseError::Empty);
1352        }
1353        if url_scheme.len() > MAX_NAME_LENGTH {
1354            return Err(ParseError::TooLong);
1355        }
1356        let mut iter = url_scheme.chars();
1357        let first_char = iter.next().unwrap();
1358        if !first_char.is_ascii_lowercase() {
1359            return Err(ParseError::InvalidValue);
1360        }
1361        if let Some(_) = iter.find(|&c| {
1362            !c.is_ascii_lowercase() && !c.is_ascii_digit() && c != '.' && c != '+' && c != '-'
1363        }) {
1364            return Err(ParseError::InvalidValue);
1365        }
1366        Ok(())
1367    }
1368
1369    pub fn as_str(&self) -> &str {
1370        &*self.0
1371    }
1372}
1373
1374impl fmt::Display for UrlScheme {
1375    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1376        fmt::Display::fmt(&self.0, f)
1377    }
1378}
1379
1380impl FromStr for UrlScheme {
1381    type Err = ParseError;
1382
1383    fn from_str(s: &str) -> Result<Self, Self::Err> {
1384        Self::new(s)
1385    }
1386}
1387
1388impl From<UrlScheme> for String {
1389    fn from(u: UrlScheme) -> String {
1390        u.0.into()
1391    }
1392}
1393
1394impl<'de> de::Deserialize<'de> for UrlScheme {
1395    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1396    where
1397        D: de::Deserializer<'de>,
1398    {
1399        struct Visitor;
1400
1401        impl<'de> de::Visitor<'de> for Visitor {
1402            type Value = UrlScheme;
1403
1404            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1405                f.write_str("a non-empty URL scheme no more than 100 characters in length")
1406            }
1407
1408            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1409            where
1410                E: de::Error,
1411            {
1412                s.parse().map_err(|err| match err {
1413                    ParseError::InvalidValue => {
1414                        E::invalid_value(de::Unexpected::Str(s), &"a valid URL scheme")
1415                    }
1416                    ParseError::TooLong | ParseError::Empty => E::invalid_length(
1417                        s.len(),
1418                        &"a non-empty URL scheme no more than 100 characters in length",
1419                    ),
1420                    e => {
1421                        panic!("unexpected parse error: {:?}", e);
1422                    }
1423                })
1424            }
1425        }
1426        deserializer.deserialize_string(Visitor)
1427    }
1428}
1429
1430/// The duration of child components in a collection. See [`Durability`].
1431///
1432/// [`Durability`]: ../../fidl_fuchsia_sys2/enum.Durability.html
1433#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
1434#[serde(rename_all = "snake_case")]
1435pub enum Durability {
1436    Transient,
1437    /// An instance is started on creation and exists until it stops.
1438    SingleRun,
1439}
1440
1441symmetrical_enums!(Durability, fdecl::Durability, Transient, SingleRun);
1442
1443/// A component instance's startup mode. See [`StartupMode`].
1444///
1445/// [`StartupMode`]: ../../fidl_fuchsia_sys2/enum.StartupMode.html
1446#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1447#[serde(rename_all = "snake_case")]
1448pub enum StartupMode {
1449    Lazy,
1450    Eager,
1451}
1452
1453impl StartupMode {
1454    pub fn is_lazy(&self) -> bool {
1455        matches!(self, StartupMode::Lazy)
1456    }
1457}
1458
1459symmetrical_enums!(StartupMode, fdecl::StartupMode, Lazy, Eager);
1460
1461impl Default for StartupMode {
1462    fn default() -> Self {
1463        Self::Lazy
1464    }
1465}
1466
1467/// A component instance's recovery policy. See [`OnTerminate`].
1468///
1469/// [`OnTerminate`]: ../../fidl_fuchsia_sys2/enum.OnTerminate.html
1470#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1471#[serde(rename_all = "snake_case")]
1472pub enum OnTerminate {
1473    None,
1474    Reboot,
1475}
1476
1477symmetrical_enums!(OnTerminate, fdecl::OnTerminate, None, Reboot);
1478
1479impl Default for OnTerminate {
1480    fn default() -> Self {
1481        Self::None
1482    }
1483}
1484
1485/// The kinds of offers that can target components in a given collection. See
1486/// [`AllowedOffers`].
1487///
1488/// [`AllowedOffers`]: ../../fidl_fuchsia_sys2/enum.AllowedOffers.html
1489#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1490#[serde(rename_all = "snake_case")]
1491pub enum AllowedOffers {
1492    StaticOnly,
1493    StaticAndDynamic,
1494}
1495
1496symmetrical_enums!(AllowedOffers, fdecl::AllowedOffers, StaticOnly, StaticAndDynamic);
1497
1498impl Default for AllowedOffers {
1499    fn default() -> Self {
1500        Self::StaticOnly
1501    }
1502}
1503
1504/// Offered dependency type. See [`DependencyType`].
1505///
1506/// [`DependencyType`]: ../../fidl_fuchsia_sys2/enum.DependencyType.html
1507#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1508#[serde(rename_all = "snake_case")]
1509pub enum DependencyType {
1510    Strong,
1511    Weak,
1512}
1513
1514symmetrical_enums!(DependencyType, fdecl::DependencyType, Strong, Weak);
1515
1516impl Default for DependencyType {
1517    fn default() -> Self {
1518        Self::Strong
1519    }
1520}
1521
1522/// Capability availability. See [`Availability`].
1523///
1524/// [`Availability`]: ../../fidl_fuchsia_sys2/enum.Availability.html
1525#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
1526#[serde(rename_all = "snake_case")]
1527pub enum Availability {
1528    Required,
1529    Optional,
1530    SameAsTarget,
1531    Transitional,
1532}
1533
1534symmetrical_enums!(
1535    Availability,
1536    fdecl::Availability,
1537    Required,
1538    Optional,
1539    SameAsTarget,
1540    Transitional
1541);
1542
1543impl Display for Availability {
1544    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1545        match self {
1546            Availability::Required => write!(f, "Required"),
1547            Availability::Optional => write!(f, "Optional"),
1548            Availability::SameAsTarget => write!(f, "SameAsTarget"),
1549            Availability::Transitional => write!(f, "Transitional"),
1550        }
1551    }
1552}
1553
1554// TODO(cgonyeo): remove this once we've soft migrated to the availability field being required.
1555impl Default for Availability {
1556    fn default() -> Self {
1557        Self::Required
1558    }
1559}
1560
1561impl PartialOrd for Availability {
1562    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1563        match (*self, *other) {
1564            (Availability::Transitional, Availability::Optional)
1565            | (Availability::Transitional, Availability::Required)
1566            | (Availability::Optional, Availability::Required) => Some(cmp::Ordering::Less),
1567            (Availability::Optional, Availability::Transitional)
1568            | (Availability::Required, Availability::Transitional)
1569            | (Availability::Required, Availability::Optional) => Some(cmp::Ordering::Greater),
1570            (Availability::Required, Availability::Required)
1571            | (Availability::Optional, Availability::Optional)
1572            | (Availability::Transitional, Availability::Transitional)
1573            | (Availability::SameAsTarget, Availability::SameAsTarget) => {
1574                Some(cmp::Ordering::Equal)
1575            }
1576            (Availability::SameAsTarget, _) | (_, Availability::SameAsTarget) => None,
1577        }
1578    }
1579}
1580
1581/// Specifies when the framework will open the protocol from the provider
1582/// component's outgoing directory when someone requests the capability. See
1583/// [`DeliveryType`].
1584///
1585/// [`DeliveryType`]: ../../fidl_fuchsia_component_decl/enum.DeliveryType.html
1586#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
1587#[serde(rename_all = "snake_case")]
1588pub enum DeliveryType {
1589    Immediate,
1590    OnReadable,
1591}
1592
1593#[cfg(fuchsia_api_level_at_least = "HEAD")]
1594impl TryFrom<fdecl::DeliveryType> for DeliveryType {
1595    type Error = fdecl::DeliveryType;
1596
1597    fn try_from(value: fdecl::DeliveryType) -> Result<Self, Self::Error> {
1598        match value {
1599            fdecl::DeliveryType::Immediate => Ok(DeliveryType::Immediate),
1600            fdecl::DeliveryType::OnReadable => Ok(DeliveryType::OnReadable),
1601            fdecl::DeliveryTypeUnknown!() => Err(value),
1602        }
1603    }
1604}
1605
1606#[cfg(fuchsia_api_level_at_least = "HEAD")]
1607impl From<DeliveryType> for fdecl::DeliveryType {
1608    fn from(value: DeliveryType) -> Self {
1609        match value {
1610            DeliveryType::Immediate => fdecl::DeliveryType::Immediate,
1611            DeliveryType::OnReadable => fdecl::DeliveryType::OnReadable,
1612        }
1613    }
1614}
1615
1616impl Display for DeliveryType {
1617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1618        match self {
1619            DeliveryType::Immediate => write!(f, "Immediate"),
1620            DeliveryType::OnReadable => write!(f, "OnReadable"),
1621        }
1622    }
1623}
1624
1625impl Default for DeliveryType {
1626    fn default() -> Self {
1627        Self::Immediate
1628    }
1629}
1630
1631#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1632#[serde(rename_all = "snake_case")]
1633pub enum StorageId {
1634    StaticInstanceId,
1635    StaticInstanceIdOrMoniker,
1636}
1637
1638symmetrical_enums!(StorageId, fdecl::StorageId, StaticInstanceId, StaticInstanceIdOrMoniker);
1639
1640/// We can't link the fuchsia-runtime crate because it's target side only, but we don't really
1641/// need to -- its HandleType is pretty much just a thin wrapper over `u8`.
1642#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1643pub struct HandleType(u8);
1644
1645impl Serialize for HandleType {
1646    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1647    where
1648        S: serde::ser::Serializer,
1649    {
1650        self.0.serialize(serializer)
1651    }
1652}
1653
1654impl From<HandleType> for u8 {
1655    fn from(h: HandleType) -> Self {
1656        h.0
1657    }
1658}
1659
1660impl From<u8> for HandleType {
1661    fn from(h: u8) -> Self {
1662        Self(h)
1663    }
1664}
1665
1666#[cfg(target_os = "fuchsia")]
1667impl From<fuchsia_runtime::HandleType> for HandleType {
1668    fn from(h: fuchsia_runtime::HandleType) -> Self {
1669        (h as u8).into()
1670    }
1671}
1672
1673impl From<HandleType> for Name {
1674    fn from(numbered_handle: HandleType) -> Self {
1675        let numbered_handle: u8 = numbered_handle.into();
1676        let numbered_handle = format!("{numbered_handle:x}");
1677        Self::new(numbered_handle).expect("numbered_handle is a valid dictionary key")
1678    }
1679}
1680
1681impl fmt::Display for HandleType {
1682    #[inline]
1683    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1684        write!(f, "0x{:x}", self.0)
1685    }
1686}
1687
1688const HANDLE_TYPE_EXPECT_STR: &str = "a uint8 from zircon/processargs.h";
1689
1690impl<'de> de::Deserialize<'de> for HandleType {
1691    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1692    where
1693        D: de::Deserializer<'de>,
1694    {
1695        struct Visitor;
1696        impl<'de> de::Visitor<'de> for Visitor {
1697            type Value = HandleType;
1698
1699            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1700                f.write_str(HANDLE_TYPE_EXPECT_STR)
1701            }
1702
1703            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
1704            where
1705                E: de::Error,
1706            {
1707                let v = v.try_into().map_err(|_| {
1708                    de::Error::invalid_value(de::Unexpected::Unsigned(v), &HANDLE_TYPE_EXPECT_STR)
1709                })?;
1710                Ok(HandleType(v))
1711            }
1712        }
1713        deserializer.deserialize_u64(Visitor)
1714    }
1715}
1716
1717/// A namespace entry type. Identical to the type in the `namespace` crate but can be used in
1718/// contexts where that crate is not included.
1719#[derive(Debug)]
1720pub struct NamespaceEntry {
1721    pub path: NamespacePath,
1722    pub directory: fidl::endpoints::ClientEnd<fio::DirectoryMarker>,
1723}
1724
1725#[cfg(test)]
1726mod tests {
1727    use super::*;
1728    use assert_matches::assert_matches;
1729    use serde_json::json;
1730    use std::collections::HashSet;
1731    use std::iter::repeat;
1732
1733    macro_rules! expect_ok {
1734        ($type_:ty, $($input:tt)+) => {
1735            assert_matches!(
1736                serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1737                Ok(_)
1738            );
1739        };
1740    }
1741
1742    macro_rules! expect_ok_no_serialize {
1743        ($type_:ty, $($input:tt)+) => {
1744            assert_matches!(
1745                ($($input)*).parse::<$type_>(),
1746                Ok(_)
1747            );
1748        };
1749    }
1750
1751    macro_rules! expect_err_no_serialize {
1752        ($type_:ty, $err:pat, $($input:tt)+) => {
1753            assert_matches!(
1754                ($($input)*).parse::<$type_>(),
1755                Err($err)
1756            );
1757        };
1758    }
1759
1760    macro_rules! expect_err {
1761        ($type_:ty, $err:pat, $($input:tt)+) => {
1762            assert_matches!(
1763                ($($input)*).parse::<$type_>(),
1764                Err($err)
1765            );
1766            assert_matches!(
1767                serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1768                Err(_)
1769            );
1770        };
1771    }
1772
1773    #[test]
1774    fn test_valid_name() {
1775        expect_ok!(Name, "foo");
1776        expect_ok!(Name, "Foo");
1777        expect_ok!(Name, "O123._-");
1778        expect_ok!(Name, "_O123._-");
1779        expect_ok!(Name, repeat("x").take(255).collect::<String>());
1780    }
1781
1782    #[test]
1783    fn test_invalid_name() {
1784        expect_err!(Name, ParseError::Empty, "");
1785        expect_err!(Name, ParseError::InvalidValue, "-");
1786        expect_err!(Name, ParseError::InvalidValue, ".");
1787        expect_err!(Name, ParseError::InvalidValue, "@&%^");
1788        expect_err!(Name, ParseError::TooLong, repeat("x").take(256).collect::<String>());
1789    }
1790
1791    #[test]
1792    fn test_valid_path() {
1793        expect_ok!(Path, "/foo");
1794        expect_ok!(Path, "/foo/bar");
1795        expect_ok!(Path, format!("/{}", repeat("x").take(100).collect::<String>()).as_str());
1796        // 2047 * 2 characters per repeat = 4094
1797        expect_ok!(Path, repeat("/x").take(2047).collect::<String>().as_str());
1798    }
1799
1800    #[test]
1801    fn test_invalid_path() {
1802        expect_err!(Path, ParseError::Empty, "");
1803        expect_err!(Path, ParseError::InvalidValue, "/");
1804        expect_err!(Path, ParseError::InvalidValue, ".");
1805        expect_err!(Path, ParseError::NoLeadingSlash, "foo");
1806        expect_err!(Path, ParseError::NoLeadingSlash, "foo/");
1807        expect_err!(Path, ParseError::InvalidValue, "/foo/");
1808        expect_err!(Path, ParseError::InvalidValue, "/foo//bar");
1809        expect_err!(Path, ParseError::InvalidSegment, "/fo\0b/bar");
1810        expect_err!(Path, ParseError::InvalidSegment, "/.");
1811        expect_err!(Path, ParseError::InvalidSegment, "/foo/.");
1812        expect_err!(
1813            Path,
1814            ParseError::InvalidSegment,
1815            format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1816        );
1817        // 2048 * 2 characters per repeat = 4096
1818        expect_err!(
1819            Path,
1820            ParseError::TooLong,
1821            repeat("/x").take(2048).collect::<String>().as_str()
1822        );
1823    }
1824
1825    #[test]
1826    fn test_path_cstring_roundtrip() {
1827        for text in ["/foo".to_owned(), "/foo/bar".to_owned(), "/x".repeat(2047)] {
1828            let path = Path::new(&text).unwrap();
1829            let cstring = CString::from(path.clone());
1830            assert_eq!(cstring.as_bytes(), text.as_bytes());
1831            assert_eq!(cstring.as_bytes_with_nul().len(), text.len() + 1);
1832            assert_eq!(cstring.as_bytes_with_nul().last(), Some(&0));
1833            assert_eq!(Path::try_from(cstring).unwrap(), path);
1834        }
1835    }
1836
1837    #[test]
1838    fn test_namespace_path_cstring_roundtrip() {
1839        for text in ["/".to_owned(), "/foo".to_owned(), "/foo/bar".to_owned(), "/x".repeat(2047)] {
1840            let path = NamespacePath::new(&text).unwrap();
1841            let cstring = CString::from(path.clone());
1842            assert_eq!(cstring.as_bytes(), text.as_bytes());
1843            assert_eq!(cstring.as_bytes_with_nul().len(), text.len() + 1);
1844            assert_eq!(cstring.as_bytes_with_nul().last(), Some(&0));
1845            assert_eq!(NamespacePath::try_from(cstring).unwrap(), path);
1846        }
1847    }
1848
1849    #[test]
1850    fn test_modified_path_cstrings() {
1851        let mut path = Path::new("/foo").unwrap();
1852        assert!(path.push(Name::new("bar").unwrap()));
1853        assert!(path.extend(RelativePath::new("baz/qux").unwrap()));
1854        assert_eq!(CString::from(path.clone()).as_bytes_with_nul(), b"/foo/bar/baz/qux\0");
1855
1856        let mut namespace_path = NamespacePath::from(path);
1857        assert_eq!(namespace_path.pop_front(), Some(Name::new("foo").unwrap()));
1858        assert_eq!(CString::from(namespace_path.clone()).as_bytes_with_nul(), b"/bar/baz/qux\0");
1859        while namespace_path.pop_front().is_some() {}
1860        assert_eq!(CString::from(namespace_path).as_bytes_with_nul(), b"/\0");
1861        assert_eq!(CString::from(NamespacePath::root()).as_bytes_with_nul(), b"/\0");
1862    }
1863
1864    #[test]
1865    fn test_name_hash() {
1866        {
1867            let n1 = Name::new("a").unwrap();
1868            let s_b = repeat("b").take(255).collect::<String>();
1869            let n2 = Name::new(&s_b).unwrap();
1870            let b1 = BorrowedName::new("a").unwrap();
1871            let b2 = BorrowedName::new(&s_b).unwrap();
1872
1873            let mut set = HashSet::new();
1874            set.insert(n1.clone());
1875            assert!(set.contains(&n1));
1876            assert!(set.contains(b1));
1877            assert!(!set.contains(&n2));
1878            assert!(!set.contains(b2));
1879            set.insert(n2.clone());
1880            assert!(set.contains(&n1));
1881            assert!(set.contains(b1));
1882            assert!(set.contains(&n2));
1883            assert!(set.contains(b2));
1884        }
1885        {
1886            let n1 = LongName::new("a").unwrap();
1887            let s_b = repeat("b").take(1024).collect::<String>();
1888            let n2 = LongName::new(&s_b).unwrap();
1889            let b1 = BorrowedLongName::new("a").unwrap();
1890            let b2 = BorrowedLongName::new(&s_b).unwrap();
1891
1892            let mut set = HashSet::new();
1893            set.insert(n1.clone());
1894            assert!(set.contains(&n1));
1895            assert!(set.contains(b1));
1896            assert!(!set.contains(&n2));
1897            assert!(!set.contains(b2));
1898            set.insert(n2.clone());
1899            assert!(set.contains(&n1));
1900            assert!(set.contains(b1));
1901            assert!(set.contains(&n2));
1902            assert!(set.contains(b2));
1903        }
1904    }
1905
1906    // Keep in sync with test_relative_path_methods()
1907    #[test]
1908    fn test_path_methods() {
1909        let dot = RelativePath::dot();
1910        let prefix = Path::new("/some/path").unwrap();
1911        let suffix = RelativePath::new("another/path").unwrap();
1912        let segment = Name::new("segment").unwrap();
1913
1914        let mut path = prefix.clone();
1915        assert!(path.extend(suffix.clone()));
1916        assert_eq!(path, "/some/path/another/path".parse().unwrap());
1917        assert_eq!(
1918            path.split(),
1919            [
1920                BorrowedName::new("some").unwrap(),
1921                BorrowedName::new("path").unwrap(),
1922                BorrowedName::new("another").unwrap(),
1923                BorrowedName::new("path").unwrap(),
1924            ]
1925        );
1926
1927        let mut path = prefix.clone();
1928        assert!(path.extend(dot.clone()));
1929        assert_eq!(path, "/some/path".parse().unwrap());
1930
1931        let mut path = prefix.clone();
1932        assert!(path.push(segment.clone()));
1933        assert_eq!(path, "/some/path/segment".parse().unwrap());
1934        assert!(path.push(segment.clone()));
1935        assert_eq!(path, "/some/path/segment/segment".parse().unwrap());
1936        assert_eq!(
1937            path.split(),
1938            [
1939                BorrowedName::new("some").unwrap(),
1940                BorrowedName::new("path").unwrap(),
1941                BorrowedName::new("segment").unwrap(),
1942                BorrowedName::new("segment").unwrap(),
1943            ]
1944        );
1945
1946        let long_path =
1947            Path::new(format!("{}/xx", repeat("/x").take(4092 / 2).collect::<String>())).unwrap();
1948        let mut path = long_path.clone();
1949        // One more than the maximum size.
1950        assert!(!path.push("a".parse().unwrap()));
1951        assert_eq!(path, long_path);
1952        assert!(!path.extend("a".parse().unwrap()));
1953        assert_eq!(path, long_path);
1954    }
1955
1956    #[test]
1957    fn test_valid_namespace_path() {
1958        expect_ok_no_serialize!(NamespacePath, "/");
1959        expect_ok_no_serialize!(NamespacePath, "/foo");
1960        expect_ok_no_serialize!(NamespacePath, "/foo/bar");
1961        expect_ok_no_serialize!(
1962            NamespacePath,
1963            format!("/{}", repeat("x").take(100).collect::<String>()).as_str()
1964        );
1965        // 2047 * 2 characters per repeat = 4094
1966        expect_ok_no_serialize!(
1967            NamespacePath,
1968            repeat("/x").take(2047).collect::<String>().as_str()
1969        );
1970    }
1971
1972    #[test]
1973    fn test_invalid_namespace_path() {
1974        expect_err_no_serialize!(NamespacePath, ParseError::Empty, "");
1975        expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, ".");
1976        expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo");
1977        expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo/");
1978        expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo/");
1979        expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo//bar");
1980        expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/fo\0b/bar");
1981        expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/.");
1982        expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/foo/.");
1983        expect_err_no_serialize!(
1984            NamespacePath,
1985            ParseError::InvalidSegment,
1986            format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1987        );
1988        // 2048 * 2 characters per repeat = 4096
1989        expect_err_no_serialize!(
1990            Path,
1991            ParseError::TooLong,
1992            repeat("/x").take(2048).collect::<String>().as_str()
1993        );
1994    }
1995
1996    #[test]
1997    fn test_path_parent_basename() {
1998        let path = Path::new("/foo").unwrap();
1999        assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/", "foo"));
2000        let path = Path::new("/foo/bar").unwrap();
2001        assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/foo", "bar"));
2002        let path = Path::new("/foo/bar/baz").unwrap();
2003        assert_eq!(
2004            (path.parent().to_string().as_str(), path.basename().as_str()),
2005            ("/foo/bar", "baz")
2006        );
2007    }
2008
2009    #[test]
2010    fn test_separated_path() {
2011        fn test_path(path: SeparatedPath, in_expected_segments: Vec<&str>) {
2012            let expected_segments: Vec<&BorrowedName> =
2013                in_expected_segments.iter().map(|s| BorrowedName::new(*s).unwrap()).collect();
2014            let segments: Vec<&BorrowedName> = path.iter_segments().collect();
2015            assert_eq!(segments, expected_segments);
2016            let borrowed_path = path.as_ref();
2017            let segments: Vec<&BorrowedName> = borrowed_path.iter_segments().collect();
2018            assert_eq!(segments, expected_segments);
2019            let owned_path = borrowed_path.to_owned();
2020            assert_eq!(path, owned_path);
2021            let expected_fmt = in_expected_segments.join("/");
2022            assert_eq!(format!("{path}"), expected_fmt);
2023            assert_eq!(format!("{owned_path}"), expected_fmt);
2024        }
2025        test_path(
2026            SeparatedPath { dirname: ".".parse().unwrap(), basename: "foo".parse().unwrap() },
2027            vec!["foo"],
2028        );
2029        test_path(
2030            SeparatedPath { dirname: "bar".parse().unwrap(), basename: "foo".parse().unwrap() },
2031            vec!["bar", "foo"],
2032        );
2033        test_path(
2034            SeparatedPath { dirname: "bar/baz".parse().unwrap(), basename: "foo".parse().unwrap() },
2035            vec!["bar", "baz", "foo"],
2036        );
2037    }
2038
2039    #[test]
2040    fn test_valid_relative_path() {
2041        expect_ok!(RelativePath, ".");
2042        expect_ok!(RelativePath, "foo");
2043        expect_ok!(RelativePath, "foo/bar");
2044        expect_ok!(RelativePath, &format!("x{}", repeat("/x").take(2047).collect::<String>()));
2045    }
2046
2047    #[test]
2048    fn test_invalid_relative_path() {
2049        expect_err!(RelativePath, ParseError::Empty, "");
2050        expect_err!(RelativePath, ParseError::InvalidValue, "/");
2051        expect_err!(RelativePath, ParseError::InvalidValue, "/foo");
2052        expect_err!(RelativePath, ParseError::InvalidValue, "foo/");
2053        expect_err!(RelativePath, ParseError::InvalidValue, "/foo/");
2054        expect_err!(RelativePath, ParseError::InvalidValue, "foo//bar");
2055        expect_err!(RelativePath, ParseError::InvalidSegment, "..");
2056        expect_err!(RelativePath, ParseError::InvalidSegment, "foo/..");
2057        expect_err!(
2058            RelativePath,
2059            ParseError::TooLong,
2060            &format!("x{}", repeat("/x").take(2048).collect::<String>())
2061        );
2062    }
2063
2064    // Keep in sync with test_path_methods()
2065    #[test]
2066    fn test_relative_path_methods() {
2067        let dot = RelativePath::dot();
2068        let prefix = RelativePath::new("some/path").unwrap();
2069        let suffix = RelativePath::new("another/path").unwrap();
2070        let segment = Name::new("segment").unwrap();
2071
2072        let mut path = prefix.clone();
2073        assert!(path.extend(suffix.clone()));
2074        assert_eq!(path, "some/path/another/path".parse().unwrap());
2075        assert_eq!(
2076            path.split(),
2077            [
2078                BorrowedName::new("some").unwrap(),
2079                BorrowedName::new("path").unwrap(),
2080                BorrowedName::new("another").unwrap(),
2081                BorrowedName::new("path").unwrap(),
2082            ]
2083        );
2084        assert_eq!(path.pop_front(), Some(Name::new("some").unwrap()));
2085        assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
2086        assert_eq!(path.pop_front(), Some(Name::new("another").unwrap()));
2087        assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
2088        assert_eq!(path.pop_front(), None);
2089
2090        let mut path = prefix.clone();
2091        assert!(path.extend(dot.clone()));
2092        assert_eq!(path, "some/path".parse().unwrap());
2093        let mut path = dot.clone();
2094        assert!(path.extend(suffix));
2095        assert_eq!(path, "another/path".parse().unwrap());
2096        let mut path = dot.clone();
2097        assert!(path.extend(dot.clone()));
2098        assert_eq!(path, RelativePath::dot());
2099
2100        let mut path = prefix.clone();
2101        assert!(path.push(segment.clone()));
2102        assert_eq!(path, "some/path/segment".parse().unwrap());
2103        assert!(path.push(segment.clone()));
2104        assert_eq!(path, "some/path/segment/segment".parse().unwrap());
2105        assert_eq!(
2106            path.split(),
2107            [
2108                BorrowedName::new("some").unwrap(),
2109                BorrowedName::new("path").unwrap(),
2110                BorrowedName::new("segment").unwrap(),
2111                BorrowedName::new("segment").unwrap(),
2112            ]
2113        );
2114
2115        let mut path = dot.clone();
2116        assert!(path.push(segment.clone()));
2117        assert_eq!(path, "segment".parse().unwrap());
2118
2119        let long_path =
2120            RelativePath::new(format!("{}x", repeat("x/").take(4094 / 2).collect::<String>()))
2121                .unwrap();
2122        let mut path = long_path.clone();
2123        // One more than the maximum size.
2124        assert!(!path.push("a".parse().unwrap()));
2125        assert_eq!(path, long_path);
2126        assert!(!path.extend("a".parse().unwrap()));
2127        assert_eq!(path, long_path);
2128    }
2129
2130    #[test]
2131    fn test_valid_url() {
2132        expect_ok!(Url, "a://foo");
2133        expect_ok!(Url, "#relative-url");
2134        expect_ok!(Url, &format!("a://{}", repeat("x").take(4092).collect::<String>()));
2135    }
2136
2137    #[test]
2138    fn test_invalid_url() {
2139        expect_err!(Url, ParseError::Empty, "");
2140        expect_err!(Url, ParseError::InvalidComponentUrl { .. }, "foo");
2141        expect_err!(
2142            Url,
2143            ParseError::TooLong,
2144            &format!("a://{}", repeat("x").take(4093).collect::<String>())
2145        );
2146    }
2147
2148    #[test]
2149    fn test_valid_url_scheme() {
2150        expect_ok!(UrlScheme, "fuch.sia-pkg+0");
2151        expect_ok!(UrlScheme, &format!("{}", repeat("f").take(255).collect::<String>()));
2152    }
2153
2154    #[test]
2155    fn test_invalid_url_scheme() {
2156        expect_err!(UrlScheme, ParseError::Empty, "");
2157        expect_err!(UrlScheme, ParseError::InvalidValue, "0fuch.sia-pkg+0");
2158        expect_err!(UrlScheme, ParseError::InvalidValue, "fuchsia_pkg");
2159        expect_err!(UrlScheme, ParseError::InvalidValue, "FUCHSIA-PKG");
2160        expect_err!(
2161            UrlScheme,
2162            ParseError::TooLong,
2163            &format!("{}", repeat("f").take(256).collect::<String>())
2164        );
2165    }
2166
2167    #[test]
2168    fn test_name_error_message() {
2169        let input = r#"
2170            "foo$"
2171        "#;
2172        let err = serde_json::from_str::<Name>(input).expect_err("must fail");
2173        assert_eq!(
2174            err.to_string(),
2175            "invalid value: string \"foo$\", expected a name \
2176            that consists of [A-Za-z0-9_.-] and starts with [A-Za-z0-9_] \
2177            at line 2 column 18"
2178        );
2179        assert_eq!(err.line(), 2);
2180        assert_eq!(err.column(), 18);
2181    }
2182
2183    #[test]
2184    fn test_path_error_message() {
2185        let input = r#"
2186            "foo";
2187        "#;
2188        let err = serde_json::from_str::<Path>(input).expect_err("must fail");
2189        assert_eq!(
2190            err.to_string(),
2191            "invalid value: string \"foo\", expected a path with leading `/` and non-empty \
2192            segments, where each segment is no \
2193            more than fuchsia.io/MAX_NAME_LENGTH bytes in length, cannot be . or .., \
2194            and cannot contain embedded NULs at line 2 column 17"
2195        );
2196
2197        assert_eq!(err.line(), 2);
2198        assert_eq!(err.column(), 17);
2199    }
2200
2201    #[test]
2202    fn test_url_error_message() {
2203        let input = r#"
2204            "foo";
2205        "#;
2206        let err = serde_json::from_str::<Url>(input).expect_err("must fail");
2207        assert_eq!(
2208            err.to_string(),
2209            "invalid value: string \"foo\", expected a valid URL at line 2 \
2210             column 17"
2211        );
2212        assert_eq!(err.line(), 2);
2213        assert_eq!(err.column(), 17);
2214    }
2215
2216    #[test]
2217    fn test_url_scheme_error_message() {
2218        let input = r#"
2219            "9fuchsia_pkg"
2220        "#;
2221        let err = serde_json::from_str::<UrlScheme>(input).expect_err("must fail");
2222        assert_eq!(
2223            err.to_string(),
2224            "invalid value: string \"9fuchsia_pkg\", expected a valid URL scheme at line 2 column 26"
2225        );
2226        assert_eq!(err.line(), 2);
2227        assert_eq!(err.column(), 26);
2228    }
2229
2230    #[test]
2231    fn test_symmetrical_enums() {
2232        mod a {
2233            #[derive(Debug, PartialEq, Eq)]
2234            pub enum Streetlight {
2235                Green,
2236                Yellow,
2237                Red,
2238            }
2239        }
2240
2241        mod b {
2242            #[derive(Debug, PartialEq, Eq)]
2243            pub enum Streetlight {
2244                Green,
2245                Yellow,
2246                Red,
2247            }
2248        }
2249
2250        symmetrical_enums!(a::Streetlight, b::Streetlight, Green, Yellow, Red);
2251
2252        assert_eq!(a::Streetlight::Green, b::Streetlight::Green.into());
2253        assert_eq!(a::Streetlight::Yellow, b::Streetlight::Yellow.into());
2254        assert_eq!(a::Streetlight::Red, b::Streetlight::Red.into());
2255        assert_eq!(b::Streetlight::Green, a::Streetlight::Green.into());
2256        assert_eq!(b::Streetlight::Yellow, a::Streetlight::Yellow.into());
2257        assert_eq!(b::Streetlight::Red, a::Streetlight::Red.into());
2258    }
2259}