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