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