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