fuchsia_pkg/
path.rs
1use crate::errors::ParsePackagePathError;
6pub use fuchsia_url::{PackageName, PackageVariant, MAX_PACKAGE_PATH_SEGMENT_BYTES};
7
8#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash)]
10pub struct PackagePath {
11 name: PackageName,
12 variant: PackageVariant,
13}
14
15impl PackagePath {
16 pub const MAX_NAME_BYTES: usize = MAX_PACKAGE_PATH_SEGMENT_BYTES;
17 pub const MAX_VARIANT_BYTES: usize = MAX_PACKAGE_PATH_SEGMENT_BYTES;
18
19 pub fn from_name_and_variant(name: PackageName, variant: PackageVariant) -> Self {
20 Self { name, variant }
21 }
22
23 pub fn name(&self) -> &PackageName {
24 &self.name
25 }
26
27 pub fn variant(&self) -> &PackageVariant {
28 &self.variant
29 }
30
31 pub fn into_name_and_variant(self) -> (PackageName, PackageVariant) {
32 (self.name, self.variant)
33 }
34}
35
36impl std::str::FromStr for PackagePath {
37 type Err = ParsePackagePathError;
38 fn from_str(s: &str) -> Result<Self, Self::Err> {
39 let (name, variant_with_leading_slash) = match (s.find('/'), s.rfind('/')) {
40 (Option::Some(l), Option::Some(r)) if l == r => s.split_at(l),
41 (Option::Some(_), Option::Some(_)) => {
42 return Err(Self::Err::TooManySegments);
43 }
44 _ => {
45 return Err(Self::Err::TooFewSegments);
46 }
47 };
48 Ok(Self::from_name_and_variant(
49 name.parse().map_err(ParsePackagePathError::PackageName)?,
50 variant_with_leading_slash[1..]
51 .parse()
52 .map_err(ParsePackagePathError::PackageVariant)?,
53 ))
54 }
55}
56
57impl std::fmt::Display for PackagePath {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "{}/{}", self.name, self.variant)
60 }
61}
62
63#[cfg(test)]
64mod test {
65 use super::*;
66 use crate::test::random_package_path;
67 use fuchsia_url::errors::PackagePathSegmentError;
68 use proptest::prelude::*;
69
70 #[test]
71 fn reject_invalid_name() {
72 let res: Result<PackagePath, _> = "/0".parse();
73 assert_eq!(res, Err(ParsePackagePathError::PackageName(PackagePathSegmentError::Empty)));
74 }
75
76 #[test]
77 fn reject_invalid_variant() {
78 let res: Result<PackagePath, _> = "valid_name/".parse();
79 assert_eq!(res, Err(ParsePackagePathError::PackageVariant(PackagePathSegmentError::Empty)));
80 }
81
82 #[test]
83 fn display() {
84 assert_eq!(
85 format!(
86 "{}",
87 PackagePath::from_name_and_variant(
88 "package-name".parse().unwrap(),
89 "package-variant".parse().unwrap()
90 )
91 ),
92 "package-name/package-variant"
93 );
94 }
95
96 #[test]
97 fn accessors() {
98 let name = "package-name".parse::<PackageName>().unwrap();
99 let variant = "package-variant".parse::<PackageVariant>().unwrap();
100 let path = PackagePath::from_name_and_variant(name.clone(), variant.clone());
101 assert_eq!(path.name(), &name);
102 assert_eq!(path.variant(), &variant);
103 }
104
105 proptest! {
106 #![proptest_config(ProptestConfig{
107 failure_persistence: None,
108 ..Default::default()
109 })]
110
111 #[test]
112 fn display_from_str_round_trip(path in random_package_path()) {
113 prop_assert_eq!(
114 &path,
115 &path.to_string().parse().unwrap()
116 );
117 }
118 }
119}