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