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, PartialOrd, Ord, Hash)]
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
1667impl fmt::Display for HandleType {
1668    #[inline]
1669    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1670        write!(f, "0x{:x}", self.0)
1671    }
1672}
1673
1674const HANDLE_TYPE_EXPECT_STR: &str = "a uint8 from zircon/processargs.h";
1675
1676impl<'de> de::Deserialize<'de> for HandleType {
1677    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1678    where
1679        D: de::Deserializer<'de>,
1680    {
1681        struct Visitor;
1682        impl<'de> de::Visitor<'de> for Visitor {
1683            type Value = HandleType;
1684
1685            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1686                f.write_str(HANDLE_TYPE_EXPECT_STR)
1687            }
1688
1689            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
1690            where
1691                E: de::Error,
1692            {
1693                let v = v.try_into().map_err(|_| {
1694                    de::Error::invalid_value(de::Unexpected::Unsigned(v), &HANDLE_TYPE_EXPECT_STR)
1695                })?;
1696                Ok(HandleType(v))
1697            }
1698        }
1699        deserializer.deserialize_u64(Visitor)
1700    }
1701}
1702
1703#[cfg(test)]
1704mod tests {
1705    use super::*;
1706    use assert_matches::assert_matches;
1707    use serde_json::json;
1708    use std::collections::HashSet;
1709    use std::iter::repeat;
1710
1711    macro_rules! expect_ok {
1712        ($type_:ty, $($input:tt)+) => {
1713            assert_matches!(
1714                serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1715                Ok(_)
1716            );
1717        };
1718    }
1719
1720    macro_rules! expect_ok_no_serialize {
1721        ($type_:ty, $($input:tt)+) => {
1722            assert_matches!(
1723                ($($input)*).parse::<$type_>(),
1724                Ok(_)
1725            );
1726        };
1727    }
1728
1729    macro_rules! expect_err_no_serialize {
1730        ($type_:ty, $err:pat, $($input:tt)+) => {
1731            assert_matches!(
1732                ($($input)*).parse::<$type_>(),
1733                Err($err)
1734            );
1735        };
1736    }
1737
1738    macro_rules! expect_err {
1739        ($type_:ty, $err:pat, $($input:tt)+) => {
1740            assert_matches!(
1741                ($($input)*).parse::<$type_>(),
1742                Err($err)
1743            );
1744            assert_matches!(
1745                serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1746                Err(_)
1747            );
1748        };
1749    }
1750
1751    #[test]
1752    fn test_valid_name() {
1753        expect_ok!(Name, "foo");
1754        expect_ok!(Name, "Foo");
1755        expect_ok!(Name, "O123._-");
1756        expect_ok!(Name, "_O123._-");
1757        expect_ok!(Name, repeat("x").take(255).collect::<String>());
1758    }
1759
1760    #[test]
1761    fn test_invalid_name() {
1762        expect_err!(Name, ParseError::Empty, "");
1763        expect_err!(Name, ParseError::InvalidValue, "-");
1764        expect_err!(Name, ParseError::InvalidValue, ".");
1765        expect_err!(Name, ParseError::InvalidValue, "@&%^");
1766        expect_err!(Name, ParseError::TooLong, repeat("x").take(256).collect::<String>());
1767    }
1768
1769    #[test]
1770    fn test_valid_path() {
1771        expect_ok!(Path, "/foo");
1772        expect_ok!(Path, "/foo/bar");
1773        expect_ok!(Path, format!("/{}", repeat("x").take(100).collect::<String>()).as_str());
1774        // 2047 * 2 characters per repeat = 4094
1775        expect_ok!(Path, repeat("/x").take(2047).collect::<String>().as_str());
1776    }
1777
1778    #[test]
1779    fn test_invalid_path() {
1780        expect_err!(Path, ParseError::Empty, "");
1781        expect_err!(Path, ParseError::InvalidValue, "/");
1782        expect_err!(Path, ParseError::InvalidValue, ".");
1783        expect_err!(Path, ParseError::NoLeadingSlash, "foo");
1784        expect_err!(Path, ParseError::NoLeadingSlash, "foo/");
1785        expect_err!(Path, ParseError::InvalidValue, "/foo/");
1786        expect_err!(Path, ParseError::InvalidValue, "/foo//bar");
1787        expect_err!(Path, ParseError::InvalidSegment, "/fo\0b/bar");
1788        expect_err!(Path, ParseError::InvalidSegment, "/.");
1789        expect_err!(Path, ParseError::InvalidSegment, "/foo/.");
1790        expect_err!(
1791            Path,
1792            ParseError::InvalidSegment,
1793            format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1794        );
1795        // 2048 * 2 characters per repeat = 4096
1796        expect_err!(
1797            Path,
1798            ParseError::TooLong,
1799            repeat("/x").take(2048).collect::<String>().as_str()
1800        );
1801    }
1802
1803    #[test]
1804    fn test_name_hash() {
1805        {
1806            let n1 = Name::new("a").unwrap();
1807            let s_b = repeat("b").take(255).collect::<String>();
1808            let n2 = Name::new(&s_b).unwrap();
1809            let b1 = BorrowedName::new("a").unwrap();
1810            let b2 = BorrowedName::new(&s_b).unwrap();
1811
1812            let mut set = HashSet::new();
1813            set.insert(n1.clone());
1814            assert!(set.contains(&n1));
1815            assert!(set.contains(b1));
1816            assert!(!set.contains(&n2));
1817            assert!(!set.contains(b2));
1818            set.insert(n2.clone());
1819            assert!(set.contains(&n1));
1820            assert!(set.contains(b1));
1821            assert!(set.contains(&n2));
1822            assert!(set.contains(b2));
1823        }
1824        {
1825            let n1 = LongName::new("a").unwrap();
1826            let s_b = repeat("b").take(1024).collect::<String>();
1827            let n2 = LongName::new(&s_b).unwrap();
1828            let b1 = BorrowedLongName::new("a").unwrap();
1829            let b2 = BorrowedLongName::new(&s_b).unwrap();
1830
1831            let mut set = HashSet::new();
1832            set.insert(n1.clone());
1833            assert!(set.contains(&n1));
1834            assert!(set.contains(b1));
1835            assert!(!set.contains(&n2));
1836            assert!(!set.contains(b2));
1837            set.insert(n2.clone());
1838            assert!(set.contains(&n1));
1839            assert!(set.contains(b1));
1840            assert!(set.contains(&n2));
1841            assert!(set.contains(b2));
1842        }
1843    }
1844
1845    // Keep in sync with test_relative_path_methods()
1846    #[test]
1847    fn test_path_methods() {
1848        let dot = RelativePath::dot();
1849        let prefix = Path::new("/some/path").unwrap();
1850        let suffix = RelativePath::new("another/path").unwrap();
1851        let segment = Name::new("segment").unwrap();
1852
1853        let mut path = prefix.clone();
1854        assert!(path.extend(suffix.clone()));
1855        assert_eq!(path, "/some/path/another/path".parse().unwrap());
1856        assert_eq!(
1857            path.split(),
1858            [
1859                BorrowedName::new("some").unwrap(),
1860                BorrowedName::new("path").unwrap(),
1861                BorrowedName::new("another").unwrap(),
1862                BorrowedName::new("path").unwrap(),
1863            ]
1864        );
1865
1866        let mut path = prefix.clone();
1867        assert!(path.extend(dot.clone()));
1868        assert_eq!(path, "/some/path".parse().unwrap());
1869
1870        let mut path = prefix.clone();
1871        assert!(path.push(segment.clone()));
1872        assert_eq!(path, "/some/path/segment".parse().unwrap());
1873        assert!(path.push(segment.clone()));
1874        assert_eq!(path, "/some/path/segment/segment".parse().unwrap());
1875        assert_eq!(
1876            path.split(),
1877            [
1878                BorrowedName::new("some").unwrap(),
1879                BorrowedName::new("path").unwrap(),
1880                BorrowedName::new("segment").unwrap(),
1881                BorrowedName::new("segment").unwrap(),
1882            ]
1883        );
1884
1885        let long_path =
1886            Path::new(format!("{}/xx", repeat("/x").take(4092 / 2).collect::<String>())).unwrap();
1887        let mut path = long_path.clone();
1888        // One more than the maximum size.
1889        assert!(!path.push("a".parse().unwrap()));
1890        assert_eq!(path, long_path);
1891        assert!(!path.extend("a".parse().unwrap()));
1892        assert_eq!(path, long_path);
1893    }
1894
1895    #[test]
1896    fn test_valid_namespace_path() {
1897        expect_ok_no_serialize!(NamespacePath, "/");
1898        expect_ok_no_serialize!(NamespacePath, "/foo");
1899        expect_ok_no_serialize!(NamespacePath, "/foo/bar");
1900        expect_ok_no_serialize!(
1901            NamespacePath,
1902            format!("/{}", repeat("x").take(100).collect::<String>()).as_str()
1903        );
1904        // 2047 * 2 characters per repeat = 4094
1905        expect_ok_no_serialize!(
1906            NamespacePath,
1907            repeat("/x").take(2047).collect::<String>().as_str()
1908        );
1909    }
1910
1911    #[test]
1912    fn test_invalid_namespace_path() {
1913        expect_err_no_serialize!(NamespacePath, ParseError::Empty, "");
1914        expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, ".");
1915        expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo");
1916        expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo/");
1917        expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo/");
1918        expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo//bar");
1919        expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/fo\0b/bar");
1920        expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/.");
1921        expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/foo/.");
1922        expect_err_no_serialize!(
1923            NamespacePath,
1924            ParseError::InvalidSegment,
1925            format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1926        );
1927        // 2048 * 2 characters per repeat = 4096
1928        expect_err_no_serialize!(
1929            Path,
1930            ParseError::TooLong,
1931            repeat("/x").take(2048).collect::<String>().as_str()
1932        );
1933    }
1934
1935    #[test]
1936    fn test_path_parent_basename() {
1937        let path = Path::new("/foo").unwrap();
1938        assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/", "foo"));
1939        let path = Path::new("/foo/bar").unwrap();
1940        assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/foo", "bar"));
1941        let path = Path::new("/foo/bar/baz").unwrap();
1942        assert_eq!(
1943            (path.parent().to_string().as_str(), path.basename().as_str()),
1944            ("/foo/bar", "baz")
1945        );
1946    }
1947
1948    #[test]
1949    fn test_separated_path() {
1950        fn test_path(path: SeparatedPath, in_expected_segments: Vec<&str>) {
1951            let expected_segments: Vec<&BorrowedName> =
1952                in_expected_segments.iter().map(|s| BorrowedName::new(*s).unwrap()).collect();
1953            let segments: Vec<&BorrowedName> = path.iter_segments().collect();
1954            assert_eq!(segments, expected_segments);
1955            let borrowed_path = path.as_ref();
1956            let segments: Vec<&BorrowedName> = borrowed_path.iter_segments().collect();
1957            assert_eq!(segments, expected_segments);
1958            let owned_path = borrowed_path.to_owned();
1959            assert_eq!(path, owned_path);
1960            let expected_fmt = in_expected_segments.join("/");
1961            assert_eq!(format!("{path}"), expected_fmt);
1962            assert_eq!(format!("{owned_path}"), expected_fmt);
1963        }
1964        test_path(
1965            SeparatedPath { dirname: ".".parse().unwrap(), basename: "foo".parse().unwrap() },
1966            vec!["foo"],
1967        );
1968        test_path(
1969            SeparatedPath { dirname: "bar".parse().unwrap(), basename: "foo".parse().unwrap() },
1970            vec!["bar", "foo"],
1971        );
1972        test_path(
1973            SeparatedPath { dirname: "bar/baz".parse().unwrap(), basename: "foo".parse().unwrap() },
1974            vec!["bar", "baz", "foo"],
1975        );
1976    }
1977
1978    #[test]
1979    fn test_valid_relative_path() {
1980        expect_ok!(RelativePath, ".");
1981        expect_ok!(RelativePath, "foo");
1982        expect_ok!(RelativePath, "foo/bar");
1983        expect_ok!(RelativePath, &format!("x{}", repeat("/x").take(2047).collect::<String>()));
1984    }
1985
1986    #[test]
1987    fn test_invalid_relative_path() {
1988        expect_err!(RelativePath, ParseError::Empty, "");
1989        expect_err!(RelativePath, ParseError::InvalidValue, "/");
1990        expect_err!(RelativePath, ParseError::InvalidValue, "/foo");
1991        expect_err!(RelativePath, ParseError::InvalidValue, "foo/");
1992        expect_err!(RelativePath, ParseError::InvalidValue, "/foo/");
1993        expect_err!(RelativePath, ParseError::InvalidValue, "foo//bar");
1994        expect_err!(RelativePath, ParseError::InvalidSegment, "..");
1995        expect_err!(RelativePath, ParseError::InvalidSegment, "foo/..");
1996        expect_err!(
1997            RelativePath,
1998            ParseError::TooLong,
1999            &format!("x{}", repeat("/x").take(2048).collect::<String>())
2000        );
2001    }
2002
2003    // Keep in sync with test_path_methods()
2004    #[test]
2005    fn test_relative_path_methods() {
2006        let dot = RelativePath::dot();
2007        let prefix = RelativePath::new("some/path").unwrap();
2008        let suffix = RelativePath::new("another/path").unwrap();
2009        let segment = Name::new("segment").unwrap();
2010
2011        let mut path = prefix.clone();
2012        assert!(path.extend(suffix.clone()));
2013        assert_eq!(path, "some/path/another/path".parse().unwrap());
2014        assert_eq!(
2015            path.split(),
2016            [
2017                BorrowedName::new("some").unwrap(),
2018                BorrowedName::new("path").unwrap(),
2019                BorrowedName::new("another").unwrap(),
2020                BorrowedName::new("path").unwrap(),
2021            ]
2022        );
2023        assert_eq!(path.pop_front(), Some(Name::new("some").unwrap()));
2024        assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
2025        assert_eq!(path.pop_front(), Some(Name::new("another").unwrap()));
2026        assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
2027        assert_eq!(path.pop_front(), None);
2028
2029        let mut path = prefix.clone();
2030        assert!(path.extend(dot.clone()));
2031        assert_eq!(path, "some/path".parse().unwrap());
2032        let mut path = dot.clone();
2033        assert!(path.extend(suffix));
2034        assert_eq!(path, "another/path".parse().unwrap());
2035        let mut path = dot.clone();
2036        assert!(path.extend(dot.clone()));
2037        assert_eq!(path, RelativePath::dot());
2038
2039        let mut path = prefix.clone();
2040        assert!(path.push(segment.clone()));
2041        assert_eq!(path, "some/path/segment".parse().unwrap());
2042        assert!(path.push(segment.clone()));
2043        assert_eq!(path, "some/path/segment/segment".parse().unwrap());
2044        assert_eq!(
2045            path.split(),
2046            [
2047                BorrowedName::new("some").unwrap(),
2048                BorrowedName::new("path").unwrap(),
2049                BorrowedName::new("segment").unwrap(),
2050                BorrowedName::new("segment").unwrap(),
2051            ]
2052        );
2053
2054        let mut path = dot.clone();
2055        assert!(path.push(segment.clone()));
2056        assert_eq!(path, "segment".parse().unwrap());
2057
2058        let long_path =
2059            RelativePath::new(format!("{}x", repeat("x/").take(4094 / 2).collect::<String>()))
2060                .unwrap();
2061        let mut path = long_path.clone();
2062        // One more than the maximum size.
2063        assert!(!path.push("a".parse().unwrap()));
2064        assert_eq!(path, long_path);
2065        assert!(!path.extend("a".parse().unwrap()));
2066        assert_eq!(path, long_path);
2067    }
2068
2069    #[test]
2070    fn test_valid_url() {
2071        expect_ok!(Url, "a://foo");
2072        expect_ok!(Url, "#relative-url");
2073        expect_ok!(Url, &format!("a://{}", repeat("x").take(4092).collect::<String>()));
2074    }
2075
2076    #[test]
2077    fn test_invalid_url() {
2078        expect_err!(Url, ParseError::Empty, "");
2079        expect_err!(Url, ParseError::InvalidComponentUrl { .. }, "foo");
2080        expect_err!(
2081            Url,
2082            ParseError::TooLong,
2083            &format!("a://{}", repeat("x").take(4093).collect::<String>())
2084        );
2085    }
2086
2087    #[test]
2088    fn test_valid_url_scheme() {
2089        expect_ok!(UrlScheme, "fuch.sia-pkg+0");
2090        expect_ok!(UrlScheme, &format!("{}", repeat("f").take(255).collect::<String>()));
2091    }
2092
2093    #[test]
2094    fn test_invalid_url_scheme() {
2095        expect_err!(UrlScheme, ParseError::Empty, "");
2096        expect_err!(UrlScheme, ParseError::InvalidValue, "0fuch.sia-pkg+0");
2097        expect_err!(UrlScheme, ParseError::InvalidValue, "fuchsia_pkg");
2098        expect_err!(UrlScheme, ParseError::InvalidValue, "FUCHSIA-PKG");
2099        expect_err!(
2100            UrlScheme,
2101            ParseError::TooLong,
2102            &format!("{}", repeat("f").take(256).collect::<String>())
2103        );
2104    }
2105
2106    #[test]
2107    fn test_name_error_message() {
2108        let input = r#"
2109            "foo$"
2110        "#;
2111        let err = serde_json::from_str::<Name>(input).expect_err("must fail");
2112        assert_eq!(
2113            err.to_string(),
2114            "invalid value: string \"foo$\", expected a name \
2115            that consists of [A-Za-z0-9_.-] and starts with [A-Za-z0-9_] \
2116            at line 2 column 18"
2117        );
2118        assert_eq!(err.line(), 2);
2119        assert_eq!(err.column(), 18);
2120    }
2121
2122    #[test]
2123    fn test_path_error_message() {
2124        let input = r#"
2125            "foo";
2126        "#;
2127        let err = serde_json::from_str::<Path>(input).expect_err("must fail");
2128        assert_eq!(
2129            err.to_string(),
2130            "invalid value: string \"foo\", expected a path with leading `/` and non-empty \
2131            segments, where each segment is no \
2132            more than fuchsia.io/MAX_NAME_LENGTH bytes in length, cannot be . or .., \
2133            and cannot contain embedded NULs at line 2 column 17"
2134        );
2135
2136        assert_eq!(err.line(), 2);
2137        assert_eq!(err.column(), 17);
2138    }
2139
2140    #[test]
2141    fn test_url_error_message() {
2142        let input = r#"
2143            "foo";
2144        "#;
2145        let err = serde_json::from_str::<Url>(input).expect_err("must fail");
2146        assert_eq!(
2147            err.to_string(),
2148            "invalid value: string \"foo\", expected a valid URL at line 2 \
2149             column 17"
2150        );
2151        assert_eq!(err.line(), 2);
2152        assert_eq!(err.column(), 17);
2153    }
2154
2155    #[test]
2156    fn test_url_scheme_error_message() {
2157        let input = r#"
2158            "9fuchsia_pkg"
2159        "#;
2160        let err = serde_json::from_str::<UrlScheme>(input).expect_err("must fail");
2161        assert_eq!(
2162            err.to_string(),
2163            "invalid value: string \"9fuchsia_pkg\", expected a valid URL scheme at line 2 column 26"
2164        );
2165        assert_eq!(err.line(), 2);
2166        assert_eq!(err.column(), 26);
2167    }
2168
2169    #[test]
2170    fn test_symmetrical_enums() {
2171        mod a {
2172            #[derive(Debug, PartialEq, Eq)]
2173            pub enum Streetlight {
2174                Green,
2175                Yellow,
2176                Red,
2177            }
2178        }
2179
2180        mod b {
2181            #[derive(Debug, PartialEq, Eq)]
2182            pub enum Streetlight {
2183                Green,
2184                Yellow,
2185                Red,
2186            }
2187        }
2188
2189        symmetrical_enums!(a::Streetlight, b::Streetlight, Green, Yellow, Red);
2190
2191        assert_eq!(a::Streetlight::Green, b::Streetlight::Green.into());
2192        assert_eq!(a::Streetlight::Yellow, b::Streetlight::Yellow.into());
2193        assert_eq!(a::Streetlight::Red, b::Streetlight::Red.into());
2194        assert_eq!(b::Streetlight::Green, a::Streetlight::Green.into());
2195        assert_eq!(b::Streetlight::Yellow, a::Streetlight::Yellow.into());
2196        assert_eq!(b::Streetlight::Red, a::Streetlight::Red.into());
2197    }
2198}