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