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