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 prefix_segments.into_iter().zip(my_segments.into_iter()).all(|(a, b)| a == b)
568 }
569
570 pub fn basename(&self) -> Option<&BorrowedName> {
572 self.0.basename()
573 }
574
575 pub fn pop_front(&mut self) -> Option<Name> {
576 self.0.pop_front()
577 }
578
579 pub fn into_relative(self) -> RelativePath {
580 self.0
581 }
582}
583
584impl IterablePath for NamespacePath {
585 fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
586 self.0.iter_segments()
587 }
588}
589
590impl serde::ser::Serialize for NamespacePath {
591 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
592 where
593 S: serde::ser::Serializer,
594 {
595 self.to_string().serialize(serializer)
596 }
597}
598
599impl TryFrom<CString> for NamespacePath {
600 type Error = ParseError;
601
602 fn try_from(path: CString) -> Result<Self, ParseError> {
603 Self::new(path.into_string().map_err(|_| ParseError::InvalidValue)?)
604 }
605}
606
607impl From<NamespacePath> for CString {
608 fn from(path: NamespacePath) -> Self {
609 unsafe { CString::from_vec_unchecked(path.to_string().as_bytes().to_owned()) }
612 }
613}
614
615impl From<NamespacePath> for String {
616 fn from(path: NamespacePath) -> Self {
617 path.to_string()
618 }
619}
620
621impl FromStr for NamespacePath {
622 type Err = ParseError;
623
624 fn from_str(path: &str) -> Result<Self, Self::Err> {
625 Self::new(path)
626 }
627}
628
629impl fmt::Debug for NamespacePath {
630 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631 write!(f, "{}", self)
632 }
633}
634
635impl fmt::Display for NamespacePath {
636 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
637 if !self.0.is_dot() { write!(f, "/{}", self.0) } else { write!(f, "/") }
638 }
639}
640
641#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
657pub struct Path(RelativePath);
658
659impl fmt::Debug for Path {
660 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
661 write!(f, "{}", self)
662 }
663}
664
665impl fmt::Display for Path {
666 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667 write!(f, "/{}", self.0)
668 }
669}
670
671impl ser::Serialize for Path {
672 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
673 where
674 S: serde::ser::Serializer,
675 {
676 self.to_string().serialize(serializer)
677 }
678}
679
680impl Path {
681 pub fn new(path: impl AsRef<str>) -> Result<Self, ParseError> {
686 let path = path.as_ref();
687 if path.is_empty() {
688 return Err(ParseError::Empty);
689 }
690 if path == "/" || path == "." {
691 return Err(ParseError::InvalidValue);
692 }
693 if !path.starts_with('/') {
694 return Err(ParseError::NoLeadingSlash);
695 }
696 if path.len() > MAX_PATH_LENGTH {
697 return Err(ParseError::TooLong);
698 }
699 let path: RelativePath = path[1..].parse()?;
700 if path.is_dot() {
701 return Err(ParseError::InvalidSegment);
703 }
704 Ok(Self(path))
705 }
706
707 pub fn split(&self) -> Vec<&BorrowedName> {
709 self.0.split()
710 }
711
712 pub fn to_path_buf(&self) -> PathBuf {
713 PathBuf::from(self.to_string())
714 }
715
716 pub fn parent(&self) -> NamespacePath {
719 let p = self.0.parent().expect("can't be root");
720 NamespacePath(p)
721 }
722
723 pub fn basename(&self) -> &BorrowedName {
724 self.0.basename().expect("can't be root")
725 }
726
727 #[must_use]
730 pub fn extend(&mut self, other: RelativePath) -> bool {
731 let rep: FlyStr = if !other.is_dot() {
732 format!("{}/{}", self.0.rep, other.rep).into()
733 } else {
734 return true;
736 };
737 if rep.len() > MAX_PATH_LENGTH - 1 {
739 return false;
740 }
741 self.0.rep = rep;
742 true
743 }
744
745 #[must_use]
748 pub fn push(&mut self, segment: Name) -> bool {
749 let rep: FlyStr = format!("{}/{}", self.0.rep, segment).into();
750 if rep.len() > MAX_PATH_LENGTH - 1 {
752 return false;
753 }
754 self.0.rep = rep;
755 true
756 }
757}
758
759impl IterablePath for Path {
760 fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
761 Box::new(self.0.iter_segments())
762 }
763}
764
765impl From<Path> for NamespacePath {
766 fn from(value: Path) -> Self {
767 Self(value.0)
768 }
769}
770
771impl FromStr for Path {
772 type Err = ParseError;
773
774 fn from_str(path: &str) -> Result<Self, Self::Err> {
775 Self::new(path)
776 }
777}
778
779impl TryFrom<CString> for Path {
780 type Error = ParseError;
781
782 fn try_from(path: CString) -> Result<Self, ParseError> {
783 Self::new(path.into_string().map_err(|_| ParseError::InvalidValue)?)
784 }
785}
786
787impl From<Path> for CString {
788 fn from(path: Path) -> Self {
789 unsafe { CString::from_vec_unchecked(path.to_string().as_bytes().to_owned()) }
792 }
793}
794
795impl From<Path> for String {
796 fn from(path: Path) -> String {
797 path.to_string()
798 }
799}
800
801impl<'de> de::Deserialize<'de> for Path {
802 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
803 where
804 D: de::Deserializer<'de>,
805 {
806 struct Visitor;
807
808 impl<'de> de::Visitor<'de> for Visitor {
809 type Value = Path;
810
811 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812 f.write_str(
813 "a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH characters \
814 in length, with a leading `/`, and containing no \
815 empty path segments",
816 )
817 }
818
819 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
820 where
821 E: de::Error,
822 {
823 s.parse().map_err(|err| match err {
824 ParseError::InvalidValue
825 | ParseError::InvalidSegment
826 | ParseError::NoLeadingSlash => E::invalid_value(
827 de::Unexpected::Str(s),
828 &"a path with leading `/` and non-empty segments, where each segment is no \
829 more than fuchsia.io/MAX_NAME_LENGTH bytes in length, cannot be . or .., \
830 and cannot contain embedded NULs",
831 ),
832 ParseError::TooLong | ParseError::Empty => E::invalid_length(
833 s.len(),
834 &"a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH bytes \
835 in length",
836 ),
837 e => {
838 panic!("unexpected parse error: {:?}", e);
839 }
840 })
841 }
842 }
843 deserializer.deserialize_string(Visitor)
844 }
845}
846
847#[derive(Eq, Ord, PartialOrd, PartialEq, Hash, Clone)]
849pub struct RelativePath {
850 rep: FlyStr,
851}
852
853impl RelativePath {
854 pub fn new(path: impl AsRef<str>) -> Result<Self, ParseError> {
856 let path: &str = path.as_ref();
857 if path == "." {
858 return Ok(Self::dot());
859 }
860 if path.is_empty() {
861 return Err(ParseError::Empty);
862 }
863 if path.len() > MAX_PATH_LENGTH {
864 return Err(ParseError::TooLong);
865 }
866 path.split('/').try_for_each(|s| {
867 Name::new(s).map(|_| ()).map_err(|e| match e {
868 ParseError::Empty => ParseError::InvalidValue,
869 _ => ParseError::InvalidSegment,
870 })
871 })?;
872 Ok(Self { rep: path.into() })
873 }
874
875 pub fn dot() -> Self {
876 Self { rep: ".".into() }
877 }
878
879 pub fn is_dot(&self) -> bool {
880 self.rep == "."
881 }
882
883 pub fn parent(&self) -> Option<Self> {
884 if self.is_dot() {
885 None
886 } else {
887 match self.rep.rfind('/') {
888 Some(idx) => Some(Self::new(&self.rep[0..idx]).unwrap()),
889 None => Some(Self::dot()),
890 }
891 }
892 }
893
894 pub fn split(&self) -> Vec<&BorrowedName> {
895 if self.is_dot() {
896 vec![]
897 } else {
898 self.rep.split('/').map(|s| BorrowedName::new_unchecked(s)).collect()
899 }
900 }
901
902 pub fn basename(&self) -> Option<&BorrowedName> {
903 if self.is_dot() {
904 None
905 } else {
906 match self.rep.rfind('/') {
907 Some(idx) => Some(BorrowedName::new_unchecked(&self.rep[idx + 1..])),
908 None => Some(BorrowedName::new_unchecked(&self.rep)),
909 }
910 }
911 }
912
913 pub fn to_path_buf(&self) -> PathBuf {
914 if self.is_dot() { PathBuf::new() } else { PathBuf::from(self.to_string()) }
915 }
916
917 #[must_use]
920 pub fn extend(&mut self, other: Self) -> bool {
921 let rep = if self.is_dot() {
922 other.rep
923 } else if !other.is_dot() {
924 format!("{}/{}", self.rep, other.rep).into()
925 } else {
926 return true;
928 };
929 if rep.len() > MAX_PATH_LENGTH {
930 return false;
931 }
932 self.rep = rep;
933 true
934 }
935
936 #[must_use]
939 pub fn push(&mut self, segment: Name) -> bool {
940 let rep: FlyStr = if self.is_dot() {
941 format!("{segment}").into()
942 } else {
943 format!("{}/{}", self.rep, segment).into()
944 };
945 if rep.len() > MAX_PATH_LENGTH {
946 return false;
947 }
948 self.rep = rep;
949 true
950 }
951
952 pub fn pop_front(&mut self) -> Option<Name> {
953 if self.is_dot() {
954 None
955 } else {
956 let (rep, front) = match self.rep.find('/') {
957 Some(idx) => {
958 let rep = self.rep[idx + 1..].into();
959 let front = Name::new_unchecked(&self.rep[0..idx]);
960 (rep, front)
961 }
962 None => (".".into(), Name::new_unchecked(&self.rep)),
963 };
964 self.rep = rep;
965 Some(front)
966 }
967 }
968}
969
970impl Default for RelativePath {
971 fn default() -> Self {
972 Self::dot()
973 }
974}
975
976impl IterablePath for RelativePath {
977 fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
978 Box::new(self.split().into_iter())
979 }
980}
981
982impl FromStr for RelativePath {
983 type Err = ParseError;
984
985 fn from_str(path: &str) -> Result<Self, Self::Err> {
986 Self::new(path)
987 }
988}
989
990impl From<RelativePath> for String {
991 fn from(path: RelativePath) -> String {
992 path.to_string()
993 }
994}
995
996impl From<Vec<Name>> for RelativePath {
997 fn from(segments: Vec<Name>) -> Self {
998 if segments.is_empty() {
999 Self::dot()
1000 } else {
1001 Self { rep: segments.iter().map(|s| s.as_str()).collect::<Vec<_>>().join("/").into() }
1002 }
1003 }
1004}
1005
1006impl From<Vec<&BorrowedName>> for RelativePath {
1007 fn from(segments: Vec<&BorrowedName>) -> Self {
1008 if segments.is_empty() {
1009 Self::dot()
1010 } else {
1011 Self {
1012 rep: segments.into_iter().map(|s| s.as_str()).collect::<Vec<_>>().join("/").into(),
1013 }
1014 }
1015 }
1016}
1017
1018impl fmt::Debug for RelativePath {
1019 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1020 write!(f, "{}", self)
1021 }
1022}
1023
1024impl fmt::Display for RelativePath {
1025 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1026 write!(f, "{}", self.rep)
1027 }
1028}
1029
1030impl ser::Serialize for RelativePath {
1031 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1032 where
1033 S: serde::ser::Serializer,
1034 {
1035 self.to_string().serialize(serializer)
1036 }
1037}
1038
1039impl<'de> de::Deserialize<'de> for RelativePath {
1040 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1041 where
1042 D: de::Deserializer<'de>,
1043 {
1044 struct Visitor;
1045
1046 impl<'de> de::Visitor<'de> for Visitor {
1047 type Value = RelativePath;
1048
1049 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1050 f.write_str(
1051 "a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH characters \
1052 in length, not starting with `/`, and containing no empty path segments",
1053 )
1054 }
1055
1056 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1057 where
1058 E: de::Error,
1059 {
1060 s.parse().map_err(|err| match err {
1061 ParseError::InvalidValue
1062 | ParseError::InvalidSegment
1063 | ParseError::NoLeadingSlash => E::invalid_value(
1064 de::Unexpected::Str(s),
1065 &"a path with no leading `/` and non-empty segments",
1066 ),
1067 ParseError::TooLong | ParseError::Empty => E::invalid_length(
1068 s.len(),
1069 &"a non-empty path no more than fuchsia.io/MAX_PATH_LENGTH characters \
1070 in length",
1071 ),
1072 e => {
1073 panic!("unexpected parse error: {:?}", e);
1074 }
1075 })
1076 }
1077 }
1078 deserializer.deserialize_string(Visitor)
1079 }
1080}
1081
1082#[derive(Debug, Clone, PartialEq, Eq)]
1086pub struct BorrowedSeparatedPath<'a> {
1087 pub dirname: &'a RelativePath,
1088 pub basename: &'a Name,
1089}
1090
1091impl BorrowedSeparatedPath<'_> {
1092 pub fn to_owned(&self) -> SeparatedPath {
1094 SeparatedPath { dirname: self.dirname.clone(), basename: self.basename.clone() }
1095 }
1096}
1097
1098impl fmt::Display for BorrowedSeparatedPath<'_> {
1099 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1100 if !self.dirname.is_dot() {
1101 write!(f, "{}/{}", self.dirname, self.basename)
1102 } else {
1103 write!(f, "{}", self.basename)
1104 }
1105 }
1106}
1107
1108impl IterablePath for BorrowedSeparatedPath<'_> {
1109 fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
1110 Box::new(self.dirname.iter_segments().chain(iter::once(self.basename as &BorrowedName)))
1111 }
1112}
1113
1114#[derive(Debug, Clone, PartialEq, Eq)]
1118pub struct SeparatedPath {
1119 pub dirname: RelativePath,
1120 pub basename: Name,
1121}
1122
1123impl SeparatedPath {
1124 pub fn as_ref(&self) -> BorrowedSeparatedPath<'_> {
1126 BorrowedSeparatedPath { dirname: &self.dirname, basename: &self.basename }
1127 }
1128}
1129
1130impl IterablePath for SeparatedPath {
1131 fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send {
1132 Box::new(self.dirname.iter_segments().chain(iter::once(&self.basename as &BorrowedName)))
1133 }
1134}
1135
1136impl fmt::Display for SeparatedPath {
1137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1138 if !self.dirname.is_dot() {
1139 write!(f, "{}/{}", self.dirname, self.basename)
1140 } else {
1141 write!(f, "{}", self.basename)
1142 }
1143 }
1144}
1145
1146pub trait IterablePath: Clone + Send + Sync {
1148 fn iter_segments(&self) -> impl DoubleEndedIterator<Item = &BorrowedName> + Send;
1150}
1151
1152#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1155pub struct Url(FlyStr);
1156
1157impl Url {
1158 pub fn new(url: impl AsRef<str> + Into<String>) -> Result<Self, ParseError> {
1162 Self::validate(url.as_ref())?;
1163 Ok(Self(FlyStr::new(url)))
1164 }
1165
1166 pub fn validate(url_str: &str) -> Result<(), ParseError> {
1168 if url_str.is_empty() {
1169 return Err(ParseError::Empty);
1170 }
1171 if url_str.len() > MAX_URL_LENGTH {
1172 return Err(ParseError::TooLong);
1173 }
1174 match url::Url::parse(url_str).map(|url| (url, false)).or_else(|err| {
1175 if err == url::ParseError::RelativeUrlWithoutBase {
1176 DEFAULT_BASE_URL.join(url_str).map(|url| (url, true))
1177 } else {
1178 Err(err)
1179 }
1180 }) {
1181 Ok((url, is_relative)) => {
1182 let mut path = url.path();
1183 if path.starts_with('/') {
1184 path = &path[1..];
1185 }
1186 if is_relative && url.fragment().is_none() {
1187 return Err(ParseError::InvalidComponentUrl {
1199 details: "Relative URL has no resource fragment.".to_string(),
1200 });
1201 }
1202 if url.host_str().unwrap_or("").is_empty()
1203 && path.is_empty()
1204 && url.fragment().is_none()
1205 {
1206 return Err(ParseError::InvalidComponentUrl {
1207 details: "URL is missing either `host`, `path`, and/or `resource`."
1208 .to_string(),
1209 });
1210 }
1211 }
1212 Err(err) => {
1213 return Err(ParseError::InvalidComponentUrl {
1214 details: format!("Malformed URL: {err:?}."),
1215 });
1216 }
1217 }
1218 Ok(())
1220 }
1221
1222 pub fn is_relative(&self) -> bool {
1223 matches!(url::Url::parse(&self.0), Err(url::ParseError::RelativeUrlWithoutBase))
1224 }
1225
1226 pub fn scheme(&self) -> Option<String> {
1227 url::Url::parse(&self.0).ok().map(|u| u.scheme().into())
1228 }
1229
1230 pub fn resource(&self) -> Option<String> {
1231 url::Url::parse(&self.0).ok().map(|u| u.fragment().map(str::to_string)).flatten()
1232 }
1233
1234 pub fn as_str(&self) -> &str {
1235 &*self.0
1236 }
1237}
1238
1239impl FromStr for Url {
1240 type Err = ParseError;
1241
1242 fn from_str(url: &str) -> Result<Self, Self::Err> {
1243 Self::new(url)
1244 }
1245}
1246
1247impl From<Url> for String {
1248 fn from(url: Url) -> String {
1249 url.0.into()
1250 }
1251}
1252
1253impl fmt::Display for Url {
1254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1255 fmt::Display::fmt(&self.0, f)
1256 }
1257}
1258
1259impl ser::Serialize for Url {
1260 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1261 where
1262 S: ser::Serializer,
1263 {
1264 self.to_string().serialize(serializer)
1265 }
1266}
1267
1268impl<'de> de::Deserialize<'de> for Url {
1269 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1270 where
1271 D: de::Deserializer<'de>,
1272 {
1273 struct Visitor;
1274
1275 impl<'de> de::Visitor<'de> for Visitor {
1276 type Value = Url;
1277
1278 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1279 f.write_str("a non-empty URL no more than 4096 characters in length")
1280 }
1281
1282 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1283 where
1284 E: de::Error,
1285 {
1286 s.parse().map_err(|err| match err {
1287 ParseError::InvalidComponentUrl { details: _ } => {
1288 E::invalid_value(de::Unexpected::Str(s), &"a valid URL")
1289 }
1290 ParseError::TooLong | ParseError::Empty => E::invalid_length(
1291 s.len(),
1292 &"a non-empty URL no more than 4096 characters in length",
1293 ),
1294 e => {
1295 panic!("unexpected parse error: {:?}", e);
1296 }
1297 })
1298 }
1299 }
1300 deserializer.deserialize_string(Visitor)
1301 }
1302}
1303
1304impl PartialEq<&str> for Url {
1305 fn eq(&self, o: &&str) -> bool {
1306 &*self.0 == *o
1307 }
1308}
1309
1310impl PartialEq<String> for Url {
1311 fn eq(&self, o: &String) -> bool {
1312 &*self.0 == *o
1313 }
1314}
1315
1316#[derive(Serialize, Clone, Debug, Eq, Hash, PartialEq)]
1318pub struct UrlScheme(FlyStr);
1319
1320impl UrlScheme {
1321 pub fn new(url_scheme: impl AsRef<str> + Into<String>) -> Result<Self, ParseError> {
1326 Self::validate(url_scheme.as_ref())?;
1327 Ok(UrlScheme(FlyStr::new(url_scheme)))
1328 }
1329
1330 pub fn validate(url_scheme: &str) -> Result<(), ParseError> {
1333 if url_scheme.is_empty() {
1334 return Err(ParseError::Empty);
1335 }
1336 if url_scheme.len() > MAX_NAME_LENGTH {
1337 return Err(ParseError::TooLong);
1338 }
1339 let mut iter = url_scheme.chars();
1340 let first_char = iter.next().unwrap();
1341 if !first_char.is_ascii_lowercase() {
1342 return Err(ParseError::InvalidValue);
1343 }
1344 if let Some(_) = iter.find(|&c| {
1345 !c.is_ascii_lowercase() && !c.is_ascii_digit() && c != '.' && c != '+' && c != '-'
1346 }) {
1347 return Err(ParseError::InvalidValue);
1348 }
1349 Ok(())
1350 }
1351
1352 pub fn as_str(&self) -> &str {
1353 &*self.0
1354 }
1355}
1356
1357impl fmt::Display for UrlScheme {
1358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359 fmt::Display::fmt(&self.0, f)
1360 }
1361}
1362
1363impl FromStr for UrlScheme {
1364 type Err = ParseError;
1365
1366 fn from_str(s: &str) -> Result<Self, Self::Err> {
1367 Self::new(s)
1368 }
1369}
1370
1371impl From<UrlScheme> for String {
1372 fn from(u: UrlScheme) -> String {
1373 u.0.into()
1374 }
1375}
1376
1377impl<'de> de::Deserialize<'de> for UrlScheme {
1378 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1379 where
1380 D: de::Deserializer<'de>,
1381 {
1382 struct Visitor;
1383
1384 impl<'de> de::Visitor<'de> for Visitor {
1385 type Value = UrlScheme;
1386
1387 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1388 f.write_str("a non-empty URL scheme no more than 100 characters in length")
1389 }
1390
1391 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
1392 where
1393 E: de::Error,
1394 {
1395 s.parse().map_err(|err| match err {
1396 ParseError::InvalidValue => {
1397 E::invalid_value(de::Unexpected::Str(s), &"a valid URL scheme")
1398 }
1399 ParseError::TooLong | ParseError::Empty => E::invalid_length(
1400 s.len(),
1401 &"a non-empty URL scheme no more than 100 characters in length",
1402 ),
1403 e => {
1404 panic!("unexpected parse error: {:?}", e);
1405 }
1406 })
1407 }
1408 }
1409 deserializer.deserialize_string(Visitor)
1410 }
1411}
1412
1413#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
1417#[serde(rename_all = "snake_case")]
1418pub enum Durability {
1419 Transient,
1420 SingleRun,
1422}
1423
1424symmetrical_enums!(Durability, fdecl::Durability, Transient, SingleRun);
1425
1426#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1430#[serde(rename_all = "snake_case")]
1431pub enum StartupMode {
1432 Lazy,
1433 Eager,
1434}
1435
1436impl StartupMode {
1437 pub fn is_lazy(&self) -> bool {
1438 matches!(self, StartupMode::Lazy)
1439 }
1440}
1441
1442symmetrical_enums!(StartupMode, fdecl::StartupMode, Lazy, Eager);
1443
1444impl Default for StartupMode {
1445 fn default() -> Self {
1446 Self::Lazy
1447 }
1448}
1449
1450#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1454#[serde(rename_all = "snake_case")]
1455pub enum OnTerminate {
1456 None,
1457 Reboot,
1458}
1459
1460symmetrical_enums!(OnTerminate, fdecl::OnTerminate, None, Reboot);
1461
1462impl Default for OnTerminate {
1463 fn default() -> Self {
1464 Self::None
1465 }
1466}
1467
1468#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1473#[serde(rename_all = "snake_case")]
1474pub enum AllowedOffers {
1475 StaticOnly,
1476 StaticAndDynamic,
1477}
1478
1479symmetrical_enums!(AllowedOffers, fdecl::AllowedOffers, StaticOnly, StaticAndDynamic);
1480
1481impl Default for AllowedOffers {
1482 fn default() -> Self {
1483 Self::StaticOnly
1484 }
1485}
1486
1487#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1491#[serde(rename_all = "snake_case")]
1492pub enum DependencyType {
1493 Strong,
1494 Weak,
1495}
1496
1497symmetrical_enums!(DependencyType, fdecl::DependencyType, Strong, Weak);
1498
1499impl Default for DependencyType {
1500 fn default() -> Self {
1501 Self::Strong
1502 }
1503}
1504
1505#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
1509#[serde(rename_all = "snake_case")]
1510pub enum Availability {
1511 Required,
1512 Optional,
1513 SameAsTarget,
1514 Transitional,
1515}
1516
1517symmetrical_enums!(
1518 Availability,
1519 fdecl::Availability,
1520 Required,
1521 Optional,
1522 SameAsTarget,
1523 Transitional
1524);
1525
1526impl Display for Availability {
1527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1528 match self {
1529 Availability::Required => write!(f, "Required"),
1530 Availability::Optional => write!(f, "Optional"),
1531 Availability::SameAsTarget => write!(f, "SameAsTarget"),
1532 Availability::Transitional => write!(f, "Transitional"),
1533 }
1534 }
1535}
1536
1537impl Default for Availability {
1539 fn default() -> Self {
1540 Self::Required
1541 }
1542}
1543
1544impl PartialOrd for Availability {
1545 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1546 match (*self, *other) {
1547 (Availability::Transitional, Availability::Optional)
1548 | (Availability::Transitional, Availability::Required)
1549 | (Availability::Optional, Availability::Required) => Some(cmp::Ordering::Less),
1550 (Availability::Optional, Availability::Transitional)
1551 | (Availability::Required, Availability::Transitional)
1552 | (Availability::Required, Availability::Optional) => Some(cmp::Ordering::Greater),
1553 (Availability::Required, Availability::Required)
1554 | (Availability::Optional, Availability::Optional)
1555 | (Availability::Transitional, Availability::Transitional)
1556 | (Availability::SameAsTarget, Availability::SameAsTarget) => {
1557 Some(cmp::Ordering::Equal)
1558 }
1559 (Availability::SameAsTarget, _) | (_, Availability::SameAsTarget) => None,
1560 }
1561 }
1562}
1563
1564#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
1570#[serde(rename_all = "snake_case")]
1571pub enum DeliveryType {
1572 Immediate,
1573 OnReadable,
1574}
1575
1576#[cfg(fuchsia_api_level_at_least = "HEAD")]
1577impl TryFrom<fdecl::DeliveryType> for DeliveryType {
1578 type Error = fdecl::DeliveryType;
1579
1580 fn try_from(value: fdecl::DeliveryType) -> Result<Self, Self::Error> {
1581 match value {
1582 fdecl::DeliveryType::Immediate => Ok(DeliveryType::Immediate),
1583 fdecl::DeliveryType::OnReadable => Ok(DeliveryType::OnReadable),
1584 fdecl::DeliveryTypeUnknown!() => Err(value),
1585 }
1586 }
1587}
1588
1589#[cfg(fuchsia_api_level_at_least = "HEAD")]
1590impl From<DeliveryType> for fdecl::DeliveryType {
1591 fn from(value: DeliveryType) -> Self {
1592 match value {
1593 DeliveryType::Immediate => fdecl::DeliveryType::Immediate,
1594 DeliveryType::OnReadable => fdecl::DeliveryType::OnReadable,
1595 }
1596 }
1597}
1598
1599impl Display for DeliveryType {
1600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1601 match self {
1602 DeliveryType::Immediate => write!(f, "Immediate"),
1603 DeliveryType::OnReadable => write!(f, "OnReadable"),
1604 }
1605 }
1606}
1607
1608impl Default for DeliveryType {
1609 fn default() -> Self {
1610 Self::Immediate
1611 }
1612}
1613
1614#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1615#[serde(rename_all = "snake_case")]
1616pub enum StorageId {
1617 StaticInstanceId,
1618 StaticInstanceIdOrMoniker,
1619}
1620
1621symmetrical_enums!(StorageId, fdecl::StorageId, StaticInstanceId, StaticInstanceIdOrMoniker);
1622
1623#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1626pub struct HandleType(u8);
1627
1628impl Serialize for HandleType {
1629 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1630 where
1631 S: serde::ser::Serializer,
1632 {
1633 self.0.serialize(serializer)
1634 }
1635}
1636
1637impl From<HandleType> for u8 {
1638 fn from(h: HandleType) -> Self {
1639 h.0
1640 }
1641}
1642
1643impl From<u8> for HandleType {
1644 fn from(h: u8) -> Self {
1645 Self(h)
1646 }
1647}
1648
1649#[cfg(target_os = "fuchsia")]
1650impl From<fuchsia_runtime::HandleType> for HandleType {
1651 fn from(h: fuchsia_runtime::HandleType) -> Self {
1652 (h as u8).into()
1653 }
1654}
1655
1656impl From<HandleType> for Name {
1657 fn from(numbered_handle: HandleType) -> Self {
1658 let numbered_handle: u8 = numbered_handle.into();
1659 let numbered_handle = format!("{numbered_handle:x}");
1660 Self::new(numbered_handle).expect("numbered_handle is a valid dictionary key")
1661 }
1662}
1663
1664const HANDLE_TYPE_EXPECT_STR: &str = "a uint8 from zircon/processargs.h";
1665
1666impl<'de> de::Deserialize<'de> for HandleType {
1667 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1668 where
1669 D: de::Deserializer<'de>,
1670 {
1671 struct Visitor;
1672 impl<'de> de::Visitor<'de> for Visitor {
1673 type Value = HandleType;
1674
1675 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1676 f.write_str(HANDLE_TYPE_EXPECT_STR)
1677 }
1678
1679 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
1680 where
1681 E: de::Error,
1682 {
1683 let v = v.try_into().map_err(|_| {
1684 de::Error::invalid_value(de::Unexpected::Unsigned(v), &HANDLE_TYPE_EXPECT_STR)
1685 })?;
1686 Ok(HandleType(v))
1687 }
1688 }
1689 deserializer.deserialize_u64(Visitor)
1690 }
1691}
1692
1693#[cfg(test)]
1694mod tests {
1695 use super::*;
1696 use assert_matches::assert_matches;
1697 use serde_json::json;
1698 use std::collections::HashSet;
1699 use std::iter::repeat;
1700
1701 macro_rules! expect_ok {
1702 ($type_:ty, $($input:tt)+) => {
1703 assert_matches!(
1704 serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1705 Ok(_)
1706 );
1707 };
1708 }
1709
1710 macro_rules! expect_ok_no_serialize {
1711 ($type_:ty, $($input:tt)+) => {
1712 assert_matches!(
1713 ($($input)*).parse::<$type_>(),
1714 Ok(_)
1715 );
1716 };
1717 }
1718
1719 macro_rules! expect_err_no_serialize {
1720 ($type_:ty, $err:pat, $($input:tt)+) => {
1721 assert_matches!(
1722 ($($input)*).parse::<$type_>(),
1723 Err($err)
1724 );
1725 };
1726 }
1727
1728 macro_rules! expect_err {
1729 ($type_:ty, $err:pat, $($input:tt)+) => {
1730 assert_matches!(
1731 ($($input)*).parse::<$type_>(),
1732 Err($err)
1733 );
1734 assert_matches!(
1735 serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1736 Err(_)
1737 );
1738 };
1739 }
1740
1741 #[test]
1742 fn test_valid_name() {
1743 expect_ok!(Name, "foo");
1744 expect_ok!(Name, "Foo");
1745 expect_ok!(Name, "O123._-");
1746 expect_ok!(Name, "_O123._-");
1747 expect_ok!(Name, repeat("x").take(255).collect::<String>());
1748 }
1749
1750 #[test]
1751 fn test_invalid_name() {
1752 expect_err!(Name, ParseError::Empty, "");
1753 expect_err!(Name, ParseError::InvalidValue, "-");
1754 expect_err!(Name, ParseError::InvalidValue, ".");
1755 expect_err!(Name, ParseError::InvalidValue, "@&%^");
1756 expect_err!(Name, ParseError::TooLong, repeat("x").take(256).collect::<String>());
1757 }
1758
1759 #[test]
1760 fn test_valid_path() {
1761 expect_ok!(Path, "/foo");
1762 expect_ok!(Path, "/foo/bar");
1763 expect_ok!(Path, format!("/{}", repeat("x").take(100).collect::<String>()).as_str());
1764 expect_ok!(Path, repeat("/x").take(2047).collect::<String>().as_str());
1766 }
1767
1768 #[test]
1769 fn test_invalid_path() {
1770 expect_err!(Path, ParseError::Empty, "");
1771 expect_err!(Path, ParseError::InvalidValue, "/");
1772 expect_err!(Path, ParseError::InvalidValue, ".");
1773 expect_err!(Path, ParseError::NoLeadingSlash, "foo");
1774 expect_err!(Path, ParseError::NoLeadingSlash, "foo/");
1775 expect_err!(Path, ParseError::InvalidValue, "/foo/");
1776 expect_err!(Path, ParseError::InvalidValue, "/foo//bar");
1777 expect_err!(Path, ParseError::InvalidSegment, "/fo\0b/bar");
1778 expect_err!(Path, ParseError::InvalidSegment, "/.");
1779 expect_err!(Path, ParseError::InvalidSegment, "/foo/.");
1780 expect_err!(
1781 Path,
1782 ParseError::InvalidSegment,
1783 format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1784 );
1785 expect_err!(
1787 Path,
1788 ParseError::TooLong,
1789 repeat("/x").take(2048).collect::<String>().as_str()
1790 );
1791 }
1792
1793 #[test]
1794 fn test_name_hash() {
1795 {
1796 let n1 = Name::new("a").unwrap();
1797 let s_b = repeat("b").take(255).collect::<String>();
1798 let n2 = Name::new(&s_b).unwrap();
1799 let b1 = BorrowedName::new("a").unwrap();
1800 let b2 = BorrowedName::new(&s_b).unwrap();
1801
1802 let mut set = HashSet::new();
1803 set.insert(n1.clone());
1804 assert!(set.contains(&n1));
1805 assert!(set.contains(b1));
1806 assert!(!set.contains(&n2));
1807 assert!(!set.contains(b2));
1808 set.insert(n2.clone());
1809 assert!(set.contains(&n1));
1810 assert!(set.contains(b1));
1811 assert!(set.contains(&n2));
1812 assert!(set.contains(b2));
1813 }
1814 {
1815 let n1 = LongName::new("a").unwrap();
1816 let s_b = repeat("b").take(1024).collect::<String>();
1817 let n2 = LongName::new(&s_b).unwrap();
1818 let b1 = BorrowedLongName::new("a").unwrap();
1819 let b2 = BorrowedLongName::new(&s_b).unwrap();
1820
1821 let mut set = HashSet::new();
1822 set.insert(n1.clone());
1823 assert!(set.contains(&n1));
1824 assert!(set.contains(b1));
1825 assert!(!set.contains(&n2));
1826 assert!(!set.contains(b2));
1827 set.insert(n2.clone());
1828 assert!(set.contains(&n1));
1829 assert!(set.contains(b1));
1830 assert!(set.contains(&n2));
1831 assert!(set.contains(b2));
1832 }
1833 }
1834
1835 #[test]
1837 fn test_path_methods() {
1838 let dot = RelativePath::dot();
1839 let prefix = Path::new("/some/path").unwrap();
1840 let suffix = RelativePath::new("another/path").unwrap();
1841 let segment = Name::new("segment").unwrap();
1842
1843 let mut path = prefix.clone();
1844 assert!(path.extend(suffix.clone()));
1845 assert_eq!(path, "/some/path/another/path".parse().unwrap());
1846 assert_eq!(
1847 path.split(),
1848 [
1849 BorrowedName::new("some").unwrap(),
1850 BorrowedName::new("path").unwrap(),
1851 BorrowedName::new("another").unwrap(),
1852 BorrowedName::new("path").unwrap(),
1853 ]
1854 );
1855
1856 let mut path = prefix.clone();
1857 assert!(path.extend(dot.clone()));
1858 assert_eq!(path, "/some/path".parse().unwrap());
1859
1860 let mut path = prefix.clone();
1861 assert!(path.push(segment.clone()));
1862 assert_eq!(path, "/some/path/segment".parse().unwrap());
1863 assert!(path.push(segment.clone()));
1864 assert_eq!(path, "/some/path/segment/segment".parse().unwrap());
1865 assert_eq!(
1866 path.split(),
1867 [
1868 BorrowedName::new("some").unwrap(),
1869 BorrowedName::new("path").unwrap(),
1870 BorrowedName::new("segment").unwrap(),
1871 BorrowedName::new("segment").unwrap(),
1872 ]
1873 );
1874
1875 let long_path =
1876 Path::new(format!("{}/xx", repeat("/x").take(4092 / 2).collect::<String>())).unwrap();
1877 let mut path = long_path.clone();
1878 assert!(!path.push("a".parse().unwrap()));
1880 assert_eq!(path, long_path);
1881 assert!(!path.extend("a".parse().unwrap()));
1882 assert_eq!(path, long_path);
1883 }
1884
1885 #[test]
1886 fn test_valid_namespace_path() {
1887 expect_ok_no_serialize!(NamespacePath, "/");
1888 expect_ok_no_serialize!(NamespacePath, "/foo");
1889 expect_ok_no_serialize!(NamespacePath, "/foo/bar");
1890 expect_ok_no_serialize!(
1891 NamespacePath,
1892 format!("/{}", repeat("x").take(100).collect::<String>()).as_str()
1893 );
1894 expect_ok_no_serialize!(
1896 NamespacePath,
1897 repeat("/x").take(2047).collect::<String>().as_str()
1898 );
1899 }
1900
1901 #[test]
1902 fn test_invalid_namespace_path() {
1903 expect_err_no_serialize!(NamespacePath, ParseError::Empty, "");
1904 expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, ".");
1905 expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo");
1906 expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo/");
1907 expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo/");
1908 expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo//bar");
1909 expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/fo\0b/bar");
1910 expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/.");
1911 expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/foo/.");
1912 expect_err_no_serialize!(
1913 NamespacePath,
1914 ParseError::InvalidSegment,
1915 format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1916 );
1917 expect_err_no_serialize!(
1919 Path,
1920 ParseError::TooLong,
1921 repeat("/x").take(2048).collect::<String>().as_str()
1922 );
1923 }
1924
1925 #[test]
1926 fn test_path_parent_basename() {
1927 let path = Path::new("/foo").unwrap();
1928 assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/", "foo"));
1929 let path = Path::new("/foo/bar").unwrap();
1930 assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/foo", "bar"));
1931 let path = Path::new("/foo/bar/baz").unwrap();
1932 assert_eq!(
1933 (path.parent().to_string().as_str(), path.basename().as_str()),
1934 ("/foo/bar", "baz")
1935 );
1936 }
1937
1938 #[test]
1939 fn test_separated_path() {
1940 fn test_path(path: SeparatedPath, in_expected_segments: Vec<&str>) {
1941 let expected_segments: Vec<&BorrowedName> =
1942 in_expected_segments.iter().map(|s| BorrowedName::new(*s).unwrap()).collect();
1943 let segments: Vec<&BorrowedName> = path.iter_segments().collect();
1944 assert_eq!(segments, expected_segments);
1945 let borrowed_path = path.as_ref();
1946 let segments: Vec<&BorrowedName> = borrowed_path.iter_segments().collect();
1947 assert_eq!(segments, expected_segments);
1948 let owned_path = borrowed_path.to_owned();
1949 assert_eq!(path, owned_path);
1950 let expected_fmt = in_expected_segments.join("/");
1951 assert_eq!(format!("{path}"), expected_fmt);
1952 assert_eq!(format!("{owned_path}"), expected_fmt);
1953 }
1954 test_path(
1955 SeparatedPath { dirname: ".".parse().unwrap(), basename: "foo".parse().unwrap() },
1956 vec!["foo"],
1957 );
1958 test_path(
1959 SeparatedPath { dirname: "bar".parse().unwrap(), basename: "foo".parse().unwrap() },
1960 vec!["bar", "foo"],
1961 );
1962 test_path(
1963 SeparatedPath { dirname: "bar/baz".parse().unwrap(), basename: "foo".parse().unwrap() },
1964 vec!["bar", "baz", "foo"],
1965 );
1966 }
1967
1968 #[test]
1969 fn test_valid_relative_path() {
1970 expect_ok!(RelativePath, ".");
1971 expect_ok!(RelativePath, "foo");
1972 expect_ok!(RelativePath, "foo/bar");
1973 expect_ok!(RelativePath, &format!("x{}", repeat("/x").take(2047).collect::<String>()));
1974 }
1975
1976 #[test]
1977 fn test_invalid_relative_path() {
1978 expect_err!(RelativePath, ParseError::Empty, "");
1979 expect_err!(RelativePath, ParseError::InvalidValue, "/");
1980 expect_err!(RelativePath, ParseError::InvalidValue, "/foo");
1981 expect_err!(RelativePath, ParseError::InvalidValue, "foo/");
1982 expect_err!(RelativePath, ParseError::InvalidValue, "/foo/");
1983 expect_err!(RelativePath, ParseError::InvalidValue, "foo//bar");
1984 expect_err!(RelativePath, ParseError::InvalidSegment, "..");
1985 expect_err!(RelativePath, ParseError::InvalidSegment, "foo/..");
1986 expect_err!(
1987 RelativePath,
1988 ParseError::TooLong,
1989 &format!("x{}", repeat("/x").take(2048).collect::<String>())
1990 );
1991 }
1992
1993 #[test]
1995 fn test_relative_path_methods() {
1996 let dot = RelativePath::dot();
1997 let prefix = RelativePath::new("some/path").unwrap();
1998 let suffix = RelativePath::new("another/path").unwrap();
1999 let segment = Name::new("segment").unwrap();
2000
2001 let mut path = prefix.clone();
2002 assert!(path.extend(suffix.clone()));
2003 assert_eq!(path, "some/path/another/path".parse().unwrap());
2004 assert_eq!(
2005 path.split(),
2006 [
2007 BorrowedName::new("some").unwrap(),
2008 BorrowedName::new("path").unwrap(),
2009 BorrowedName::new("another").unwrap(),
2010 BorrowedName::new("path").unwrap(),
2011 ]
2012 );
2013 assert_eq!(path.pop_front(), Some(Name::new("some").unwrap()));
2014 assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
2015 assert_eq!(path.pop_front(), Some(Name::new("another").unwrap()));
2016 assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
2017 assert_eq!(path.pop_front(), None);
2018
2019 let mut path = prefix.clone();
2020 assert!(path.extend(dot.clone()));
2021 assert_eq!(path, "some/path".parse().unwrap());
2022 let mut path = dot.clone();
2023 assert!(path.extend(suffix));
2024 assert_eq!(path, "another/path".parse().unwrap());
2025 let mut path = dot.clone();
2026 assert!(path.extend(dot.clone()));
2027 assert_eq!(path, RelativePath::dot());
2028
2029 let mut path = prefix.clone();
2030 assert!(path.push(segment.clone()));
2031 assert_eq!(path, "some/path/segment".parse().unwrap());
2032 assert!(path.push(segment.clone()));
2033 assert_eq!(path, "some/path/segment/segment".parse().unwrap());
2034 assert_eq!(
2035 path.split(),
2036 [
2037 BorrowedName::new("some").unwrap(),
2038 BorrowedName::new("path").unwrap(),
2039 BorrowedName::new("segment").unwrap(),
2040 BorrowedName::new("segment").unwrap(),
2041 ]
2042 );
2043
2044 let mut path = dot.clone();
2045 assert!(path.push(segment.clone()));
2046 assert_eq!(path, "segment".parse().unwrap());
2047
2048 let long_path =
2049 RelativePath::new(format!("{}x", repeat("x/").take(4094 / 2).collect::<String>()))
2050 .unwrap();
2051 let mut path = long_path.clone();
2052 assert!(!path.push("a".parse().unwrap()));
2054 assert_eq!(path, long_path);
2055 assert!(!path.extend("a".parse().unwrap()));
2056 assert_eq!(path, long_path);
2057 }
2058
2059 #[test]
2060 fn test_valid_url() {
2061 expect_ok!(Url, "a://foo");
2062 expect_ok!(Url, "#relative-url");
2063 expect_ok!(Url, &format!("a://{}", repeat("x").take(4092).collect::<String>()));
2064 }
2065
2066 #[test]
2067 fn test_invalid_url() {
2068 expect_err!(Url, ParseError::Empty, "");
2069 expect_err!(Url, ParseError::InvalidComponentUrl { .. }, "foo");
2070 expect_err!(
2071 Url,
2072 ParseError::TooLong,
2073 &format!("a://{}", repeat("x").take(4093).collect::<String>())
2074 );
2075 }
2076
2077 #[test]
2078 fn test_valid_url_scheme() {
2079 expect_ok!(UrlScheme, "fuch.sia-pkg+0");
2080 expect_ok!(UrlScheme, &format!("{}", repeat("f").take(255).collect::<String>()));
2081 }
2082
2083 #[test]
2084 fn test_invalid_url_scheme() {
2085 expect_err!(UrlScheme, ParseError::Empty, "");
2086 expect_err!(UrlScheme, ParseError::InvalidValue, "0fuch.sia-pkg+0");
2087 expect_err!(UrlScheme, ParseError::InvalidValue, "fuchsia_pkg");
2088 expect_err!(UrlScheme, ParseError::InvalidValue, "FUCHSIA-PKG");
2089 expect_err!(
2090 UrlScheme,
2091 ParseError::TooLong,
2092 &format!("{}", repeat("f").take(256).collect::<String>())
2093 );
2094 }
2095
2096 #[test]
2097 fn test_name_error_message() {
2098 let input = r#"
2099 "foo$"
2100 "#;
2101 let err = serde_json::from_str::<Name>(input).expect_err("must fail");
2102 assert_eq!(
2103 err.to_string(),
2104 "invalid value: string \"foo$\", expected a name \
2105 that consists of [A-Za-z0-9_.-] and starts with [A-Za-z0-9_] \
2106 at line 2 column 18"
2107 );
2108 assert_eq!(err.line(), 2);
2109 assert_eq!(err.column(), 18);
2110 }
2111
2112 #[test]
2113 fn test_path_error_message() {
2114 let input = r#"
2115 "foo";
2116 "#;
2117 let err = serde_json::from_str::<Path>(input).expect_err("must fail");
2118 assert_eq!(
2119 err.to_string(),
2120 "invalid value: string \"foo\", expected a path with leading `/` and non-empty \
2121 segments, where each segment is no \
2122 more than fuchsia.io/MAX_NAME_LENGTH bytes in length, cannot be . or .., \
2123 and cannot contain embedded NULs at line 2 column 17"
2124 );
2125
2126 assert_eq!(err.line(), 2);
2127 assert_eq!(err.column(), 17);
2128 }
2129
2130 #[test]
2131 fn test_url_error_message() {
2132 let input = r#"
2133 "foo";
2134 "#;
2135 let err = serde_json::from_str::<Url>(input).expect_err("must fail");
2136 assert_eq!(
2137 err.to_string(),
2138 "invalid value: string \"foo\", expected a valid URL at line 2 \
2139 column 17"
2140 );
2141 assert_eq!(err.line(), 2);
2142 assert_eq!(err.column(), 17);
2143 }
2144
2145 #[test]
2146 fn test_url_scheme_error_message() {
2147 let input = r#"
2148 "9fuchsia_pkg"
2149 "#;
2150 let err = serde_json::from_str::<UrlScheme>(input).expect_err("must fail");
2151 assert_eq!(
2152 err.to_string(),
2153 "invalid value: string \"9fuchsia_pkg\", expected a valid URL scheme at line 2 column 26"
2154 );
2155 assert_eq!(err.line(), 2);
2156 assert_eq!(err.column(), 26);
2157 }
2158
2159 #[test]
2160 fn test_symmetrical_enums() {
2161 mod a {
2162 #[derive(Debug, PartialEq, Eq)]
2163 pub enum Streetlight {
2164 Green,
2165 Yellow,
2166 Red,
2167 }
2168 }
2169
2170 mod b {
2171 #[derive(Debug, PartialEq, Eq)]
2172 pub enum Streetlight {
2173 Green,
2174 Yellow,
2175 Red,
2176 }
2177 }
2178
2179 symmetrical_enums!(a::Streetlight, b::Streetlight, Green, Yellow, Red);
2180
2181 assert_eq!(a::Streetlight::Green, b::Streetlight::Green.into());
2182 assert_eq!(a::Streetlight::Yellow, b::Streetlight::Yellow.into());
2183 assert_eq!(a::Streetlight::Red, b::Streetlight::Red.into());
2184 assert_eq!(b::Streetlight::Green, a::Streetlight::Green.into());
2185 assert_eq!(b::Streetlight::Yellow, a::Streetlight::Yellow.into());
2186 assert_eq!(b::Streetlight::Red, a::Streetlight::Red.into());
2187 }
2188}