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#[cfg(test)]
1624mod tests {
1625 use super::*;
1626 use assert_matches::assert_matches;
1627 use serde_json::json;
1628 use std::collections::HashSet;
1629 use std::iter::repeat;
1630
1631 macro_rules! expect_ok {
1632 ($type_:ty, $($input:tt)+) => {
1633 assert_matches!(
1634 serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1635 Ok(_)
1636 );
1637 };
1638 }
1639
1640 macro_rules! expect_ok_no_serialize {
1641 ($type_:ty, $($input:tt)+) => {
1642 assert_matches!(
1643 ($($input)*).parse::<$type_>(),
1644 Ok(_)
1645 );
1646 };
1647 }
1648
1649 macro_rules! expect_err_no_serialize {
1650 ($type_:ty, $err:pat, $($input:tt)+) => {
1651 assert_matches!(
1652 ($($input)*).parse::<$type_>(),
1653 Err($err)
1654 );
1655 };
1656 }
1657
1658 macro_rules! expect_err {
1659 ($type_:ty, $err:pat, $($input:tt)+) => {
1660 assert_matches!(
1661 ($($input)*).parse::<$type_>(),
1662 Err($err)
1663 );
1664 assert_matches!(
1665 serde_json::from_str::<$type_>(&json!($($input)*).to_string()),
1666 Err(_)
1667 );
1668 };
1669 }
1670
1671 #[test]
1672 fn test_valid_name() {
1673 expect_ok!(Name, "foo");
1674 expect_ok!(Name, "Foo");
1675 expect_ok!(Name, "O123._-");
1676 expect_ok!(Name, "_O123._-");
1677 expect_ok!(Name, repeat("x").take(255).collect::<String>());
1678 }
1679
1680 #[test]
1681 fn test_invalid_name() {
1682 expect_err!(Name, ParseError::Empty, "");
1683 expect_err!(Name, ParseError::InvalidValue, "-");
1684 expect_err!(Name, ParseError::InvalidValue, ".");
1685 expect_err!(Name, ParseError::InvalidValue, "@&%^");
1686 expect_err!(Name, ParseError::TooLong, repeat("x").take(256).collect::<String>());
1687 }
1688
1689 #[test]
1690 fn test_valid_path() {
1691 expect_ok!(Path, "/foo");
1692 expect_ok!(Path, "/foo/bar");
1693 expect_ok!(Path, format!("/{}", repeat("x").take(100).collect::<String>()).as_str());
1694 expect_ok!(Path, repeat("/x").take(2047).collect::<String>().as_str());
1696 }
1697
1698 #[test]
1699 fn test_invalid_path() {
1700 expect_err!(Path, ParseError::Empty, "");
1701 expect_err!(Path, ParseError::InvalidValue, "/");
1702 expect_err!(Path, ParseError::InvalidValue, ".");
1703 expect_err!(Path, ParseError::NoLeadingSlash, "foo");
1704 expect_err!(Path, ParseError::NoLeadingSlash, "foo/");
1705 expect_err!(Path, ParseError::InvalidValue, "/foo/");
1706 expect_err!(Path, ParseError::InvalidValue, "/foo//bar");
1707 expect_err!(Path, ParseError::InvalidSegment, "/fo\0b/bar");
1708 expect_err!(Path, ParseError::InvalidSegment, "/.");
1709 expect_err!(Path, ParseError::InvalidSegment, "/foo/.");
1710 expect_err!(
1711 Path,
1712 ParseError::InvalidSegment,
1713 format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1714 );
1715 expect_err!(
1717 Path,
1718 ParseError::TooLong,
1719 repeat("/x").take(2048).collect::<String>().as_str()
1720 );
1721 }
1722
1723 #[test]
1724 fn test_name_hash() {
1725 {
1726 let n1 = Name::new("a").unwrap();
1727 let s_b = repeat("b").take(255).collect::<String>();
1728 let n2 = Name::new(&s_b).unwrap();
1729 let b1 = BorrowedName::new("a").unwrap();
1730 let b2 = BorrowedName::new(&s_b).unwrap();
1731
1732 let mut set = HashSet::new();
1733 set.insert(n1.clone());
1734 assert!(set.contains(&n1));
1735 assert!(set.contains(b1));
1736 assert!(!set.contains(&n2));
1737 assert!(!set.contains(b2));
1738 set.insert(n2.clone());
1739 assert!(set.contains(&n1));
1740 assert!(set.contains(b1));
1741 assert!(set.contains(&n2));
1742 assert!(set.contains(b2));
1743 }
1744 {
1745 let n1 = LongName::new("a").unwrap();
1746 let s_b = repeat("b").take(1024).collect::<String>();
1747 let n2 = LongName::new(&s_b).unwrap();
1748 let b1 = BorrowedLongName::new("a").unwrap();
1749 let b2 = BorrowedLongName::new(&s_b).unwrap();
1750
1751 let mut set = HashSet::new();
1752 set.insert(n1.clone());
1753 assert!(set.contains(&n1));
1754 assert!(set.contains(b1));
1755 assert!(!set.contains(&n2));
1756 assert!(!set.contains(b2));
1757 set.insert(n2.clone());
1758 assert!(set.contains(&n1));
1759 assert!(set.contains(b1));
1760 assert!(set.contains(&n2));
1761 assert!(set.contains(b2));
1762 }
1763 }
1764
1765 #[test]
1767 fn test_path_methods() {
1768 let dot = RelativePath::dot();
1769 let prefix = Path::new("/some/path").unwrap();
1770 let suffix = RelativePath::new("another/path").unwrap();
1771 let segment = Name::new("segment").unwrap();
1772
1773 let mut path = prefix.clone();
1774 assert!(path.extend(suffix.clone()));
1775 assert_eq!(path, "/some/path/another/path".parse().unwrap());
1776 assert_eq!(
1777 path.split(),
1778 [
1779 BorrowedName::new("some").unwrap(),
1780 BorrowedName::new("path").unwrap(),
1781 BorrowedName::new("another").unwrap(),
1782 BorrowedName::new("path").unwrap(),
1783 ]
1784 );
1785
1786 let mut path = prefix.clone();
1787 assert!(path.extend(dot.clone()));
1788 assert_eq!(path, "/some/path".parse().unwrap());
1789
1790 let mut path = prefix.clone();
1791 assert!(path.push(segment.clone()));
1792 assert_eq!(path, "/some/path/segment".parse().unwrap());
1793 assert!(path.push(segment.clone()));
1794 assert_eq!(path, "/some/path/segment/segment".parse().unwrap());
1795 assert_eq!(
1796 path.split(),
1797 [
1798 BorrowedName::new("some").unwrap(),
1799 BorrowedName::new("path").unwrap(),
1800 BorrowedName::new("segment").unwrap(),
1801 BorrowedName::new("segment").unwrap(),
1802 ]
1803 );
1804
1805 let long_path =
1806 Path::new(format!("{}/xx", repeat("/x").take(4092 / 2).collect::<String>())).unwrap();
1807 let mut path = long_path.clone();
1808 assert!(!path.push("a".parse().unwrap()));
1810 assert_eq!(path, long_path);
1811 assert!(!path.extend("a".parse().unwrap()));
1812 assert_eq!(path, long_path);
1813 }
1814
1815 #[test]
1816 fn test_valid_namespace_path() {
1817 expect_ok_no_serialize!(NamespacePath, "/");
1818 expect_ok_no_serialize!(NamespacePath, "/foo");
1819 expect_ok_no_serialize!(NamespacePath, "/foo/bar");
1820 expect_ok_no_serialize!(
1821 NamespacePath,
1822 format!("/{}", repeat("x").take(100).collect::<String>()).as_str()
1823 );
1824 expect_ok_no_serialize!(
1826 NamespacePath,
1827 repeat("/x").take(2047).collect::<String>().as_str()
1828 );
1829 }
1830
1831 #[test]
1832 fn test_invalid_namespace_path() {
1833 expect_err_no_serialize!(NamespacePath, ParseError::Empty, "");
1834 expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, ".");
1835 expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo");
1836 expect_err_no_serialize!(NamespacePath, ParseError::NoLeadingSlash, "foo/");
1837 expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo/");
1838 expect_err_no_serialize!(NamespacePath, ParseError::InvalidValue, "/foo//bar");
1839 expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/fo\0b/bar");
1840 expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/.");
1841 expect_err_no_serialize!(NamespacePath, ParseError::InvalidSegment, "/foo/.");
1842 expect_err_no_serialize!(
1843 NamespacePath,
1844 ParseError::InvalidSegment,
1845 format!("/{}", repeat("x").take(256).collect::<String>()).as_str()
1846 );
1847 expect_err_no_serialize!(
1849 Path,
1850 ParseError::TooLong,
1851 repeat("/x").take(2048).collect::<String>().as_str()
1852 );
1853 }
1854
1855 #[test]
1856 fn test_path_parent_basename() {
1857 let path = Path::new("/foo").unwrap();
1858 assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/", "foo"));
1859 let path = Path::new("/foo/bar").unwrap();
1860 assert_eq!((path.parent().to_string().as_str(), path.basename().as_str()), ("/foo", "bar"));
1861 let path = Path::new("/foo/bar/baz").unwrap();
1862 assert_eq!(
1863 (path.parent().to_string().as_str(), path.basename().as_str()),
1864 ("/foo/bar", "baz")
1865 );
1866 }
1867
1868 #[test]
1869 fn test_separated_path() {
1870 fn test_path(path: SeparatedPath, in_expected_segments: Vec<&str>) {
1871 let expected_segments: Vec<&BorrowedName> =
1872 in_expected_segments.iter().map(|s| BorrowedName::new(*s).unwrap()).collect();
1873 let segments: Vec<&BorrowedName> = path.iter_segments().collect();
1874 assert_eq!(segments, expected_segments);
1875 let borrowed_path = path.as_ref();
1876 let segments: Vec<&BorrowedName> = borrowed_path.iter_segments().collect();
1877 assert_eq!(segments, expected_segments);
1878 let owned_path = borrowed_path.to_owned();
1879 assert_eq!(path, owned_path);
1880 let expected_fmt = in_expected_segments.join("/");
1881 assert_eq!(format!("{path}"), expected_fmt);
1882 assert_eq!(format!("{owned_path}"), expected_fmt);
1883 }
1884 test_path(
1885 SeparatedPath { dirname: ".".parse().unwrap(), basename: "foo".parse().unwrap() },
1886 vec!["foo"],
1887 );
1888 test_path(
1889 SeparatedPath { dirname: "bar".parse().unwrap(), basename: "foo".parse().unwrap() },
1890 vec!["bar", "foo"],
1891 );
1892 test_path(
1893 SeparatedPath { dirname: "bar/baz".parse().unwrap(), basename: "foo".parse().unwrap() },
1894 vec!["bar", "baz", "foo"],
1895 );
1896 }
1897
1898 #[test]
1899 fn test_valid_relative_path() {
1900 expect_ok!(RelativePath, ".");
1901 expect_ok!(RelativePath, "foo");
1902 expect_ok!(RelativePath, "foo/bar");
1903 expect_ok!(RelativePath, &format!("x{}", repeat("/x").take(2047).collect::<String>()));
1904 }
1905
1906 #[test]
1907 fn test_invalid_relative_path() {
1908 expect_err!(RelativePath, ParseError::Empty, "");
1909 expect_err!(RelativePath, ParseError::InvalidValue, "/");
1910 expect_err!(RelativePath, ParseError::InvalidValue, "/foo");
1911 expect_err!(RelativePath, ParseError::InvalidValue, "foo/");
1912 expect_err!(RelativePath, ParseError::InvalidValue, "/foo/");
1913 expect_err!(RelativePath, ParseError::InvalidValue, "foo//bar");
1914 expect_err!(RelativePath, ParseError::InvalidSegment, "..");
1915 expect_err!(RelativePath, ParseError::InvalidSegment, "foo/..");
1916 expect_err!(
1917 RelativePath,
1918 ParseError::TooLong,
1919 &format!("x{}", repeat("/x").take(2048).collect::<String>())
1920 );
1921 }
1922
1923 #[test]
1925 fn test_relative_path_methods() {
1926 let dot = RelativePath::dot();
1927 let prefix = RelativePath::new("some/path").unwrap();
1928 let suffix = RelativePath::new("another/path").unwrap();
1929 let segment = Name::new("segment").unwrap();
1930
1931 let mut path = prefix.clone();
1932 assert!(path.extend(suffix.clone()));
1933 assert_eq!(path, "some/path/another/path".parse().unwrap());
1934 assert_eq!(
1935 path.split(),
1936 [
1937 BorrowedName::new("some").unwrap(),
1938 BorrowedName::new("path").unwrap(),
1939 BorrowedName::new("another").unwrap(),
1940 BorrowedName::new("path").unwrap(),
1941 ]
1942 );
1943 assert_eq!(path.pop_front(), Some(Name::new("some").unwrap()));
1944 assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
1945 assert_eq!(path.pop_front(), Some(Name::new("another").unwrap()));
1946 assert_eq!(path.pop_front(), Some(Name::new("path").unwrap()));
1947 assert_eq!(path.pop_front(), None);
1948
1949 let mut path = prefix.clone();
1950 assert!(path.extend(dot.clone()));
1951 assert_eq!(path, "some/path".parse().unwrap());
1952 let mut path = dot.clone();
1953 assert!(path.extend(suffix));
1954 assert_eq!(path, "another/path".parse().unwrap());
1955 let mut path = dot.clone();
1956 assert!(path.extend(dot.clone()));
1957 assert_eq!(path, RelativePath::dot());
1958
1959 let mut path = prefix.clone();
1960 assert!(path.push(segment.clone()));
1961 assert_eq!(path, "some/path/segment".parse().unwrap());
1962 assert!(path.push(segment.clone()));
1963 assert_eq!(path, "some/path/segment/segment".parse().unwrap());
1964 assert_eq!(
1965 path.split(),
1966 [
1967 BorrowedName::new("some").unwrap(),
1968 BorrowedName::new("path").unwrap(),
1969 BorrowedName::new("segment").unwrap(),
1970 BorrowedName::new("segment").unwrap(),
1971 ]
1972 );
1973
1974 let mut path = dot.clone();
1975 assert!(path.push(segment.clone()));
1976 assert_eq!(path, "segment".parse().unwrap());
1977
1978 let long_path =
1979 RelativePath::new(format!("{}x", repeat("x/").take(4094 / 2).collect::<String>()))
1980 .unwrap();
1981 let mut path = long_path.clone();
1982 assert!(!path.push("a".parse().unwrap()));
1984 assert_eq!(path, long_path);
1985 assert!(!path.extend("a".parse().unwrap()));
1986 assert_eq!(path, long_path);
1987 }
1988
1989 #[test]
1990 fn test_valid_url() {
1991 expect_ok!(Url, "a://foo");
1992 expect_ok!(Url, "#relative-url");
1993 expect_ok!(Url, &format!("a://{}", repeat("x").take(4092).collect::<String>()));
1994 }
1995
1996 #[test]
1997 fn test_invalid_url() {
1998 expect_err!(Url, ParseError::Empty, "");
1999 expect_err!(Url, ParseError::InvalidComponentUrl { .. }, "foo");
2000 expect_err!(
2001 Url,
2002 ParseError::TooLong,
2003 &format!("a://{}", repeat("x").take(4093).collect::<String>())
2004 );
2005 }
2006
2007 #[test]
2008 fn test_valid_url_scheme() {
2009 expect_ok!(UrlScheme, "fuch.sia-pkg+0");
2010 expect_ok!(UrlScheme, &format!("{}", repeat("f").take(255).collect::<String>()));
2011 }
2012
2013 #[test]
2014 fn test_invalid_url_scheme() {
2015 expect_err!(UrlScheme, ParseError::Empty, "");
2016 expect_err!(UrlScheme, ParseError::InvalidValue, "0fuch.sia-pkg+0");
2017 expect_err!(UrlScheme, ParseError::InvalidValue, "fuchsia_pkg");
2018 expect_err!(UrlScheme, ParseError::InvalidValue, "FUCHSIA-PKG");
2019 expect_err!(
2020 UrlScheme,
2021 ParseError::TooLong,
2022 &format!("{}", repeat("f").take(256).collect::<String>())
2023 );
2024 }
2025
2026 #[test]
2027 fn test_name_error_message() {
2028 let input = r#"
2029 "foo$"
2030 "#;
2031 let err = serde_json::from_str::<Name>(input).expect_err("must fail");
2032 assert_eq!(
2033 err.to_string(),
2034 "invalid value: string \"foo$\", expected a name \
2035 that consists of [A-Za-z0-9_.-] and starts with [A-Za-z0-9_] \
2036 at line 2 column 18"
2037 );
2038 assert_eq!(err.line(), 2);
2039 assert_eq!(err.column(), 18);
2040 }
2041
2042 #[test]
2043 fn test_path_error_message() {
2044 let input = r#"
2045 "foo";
2046 "#;
2047 let err = serde_json::from_str::<Path>(input).expect_err("must fail");
2048 assert_eq!(
2049 err.to_string(),
2050 "invalid value: string \"foo\", expected a path with leading `/` and non-empty \
2051 segments, where each segment is no \
2052 more than fuchsia.io/MAX_NAME_LENGTH bytes in length, cannot be . or .., \
2053 and cannot contain embedded NULs at line 2 column 17"
2054 );
2055
2056 assert_eq!(err.line(), 2);
2057 assert_eq!(err.column(), 17);
2058 }
2059
2060 #[test]
2061 fn test_url_error_message() {
2062 let input = r#"
2063 "foo";
2064 "#;
2065 let err = serde_json::from_str::<Url>(input).expect_err("must fail");
2066 assert_eq!(
2067 err.to_string(),
2068 "invalid value: string \"foo\", expected a valid URL at line 2 \
2069 column 17"
2070 );
2071 assert_eq!(err.line(), 2);
2072 assert_eq!(err.column(), 17);
2073 }
2074
2075 #[test]
2076 fn test_url_scheme_error_message() {
2077 let input = r#"
2078 "9fuchsia_pkg"
2079 "#;
2080 let err = serde_json::from_str::<UrlScheme>(input).expect_err("must fail");
2081 assert_eq!(
2082 err.to_string(),
2083 "invalid value: string \"9fuchsia_pkg\", expected a valid URL scheme at line 2 column 26"
2084 );
2085 assert_eq!(err.line(), 2);
2086 assert_eq!(err.column(), 26);
2087 }
2088
2089 #[test]
2090 fn test_symmetrical_enums() {
2091 mod a {
2092 #[derive(Debug, PartialEq, Eq)]
2093 pub enum Streetlight {
2094 Green,
2095 Yellow,
2096 Red,
2097 }
2098 }
2099
2100 mod b {
2101 #[derive(Debug, PartialEq, Eq)]
2102 pub enum Streetlight {
2103 Green,
2104 Yellow,
2105 Red,
2106 }
2107 }
2108
2109 symmetrical_enums!(a::Streetlight, b::Streetlight, Green, Yellow, Red);
2110
2111 assert_eq!(a::Streetlight::Green, b::Streetlight::Green.into());
2112 assert_eq!(a::Streetlight::Yellow, b::Streetlight::Yellow.into());
2113 assert_eq!(a::Streetlight::Red, b::Streetlight::Red.into());
2114 assert_eq!(b::Streetlight::Green, a::Streetlight::Green.into());
2115 assert_eq!(b::Streetlight::Yellow, a::Streetlight::Yellow.into());
2116 assert_eq!(b::Streetlight::Red, a::Streetlight::Red.into());
2117 }
2118}