Skip to main content

cm_types/
lib.rs

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