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