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