fuchsia_url/
fuchsia_pkg_component_url.rs1use crate::errors::ParseError;
6use crate::{
7 FuchsiaPkgAbsoluteComponentUrl, FuchsiaPkgPackageUrl, RelativeComponentUrl, Resource, UrlParts,
8};
9
10#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct FuchsiaPkgComponentUrl {
15 package: FuchsiaPkgPackageUrl,
16 resource: Resource,
17}
18
19impl FuchsiaPkgComponentUrl {
20 pub fn parse(url: &str) -> Result<Self, ParseError> {
22 let parts = UrlParts::parse(url)?;
23 Ok(if parts.scheme.is_some() {
24 let (absolute, resource) =
25 FuchsiaPkgAbsoluteComponentUrl::from_parts(parts)?.into_package_and_resource();
26 Self { package: absolute.into(), resource }
27 } else {
28 let (relative, resource) =
29 RelativeComponentUrl::from_parts(parts)?.into_package_and_resource();
30 Self { package: relative.into(), resource }
31 })
32 }
33
34 pub fn package_url(&self) -> &FuchsiaPkgPackageUrl {
36 &self.package
37 }
38
39 pub fn resource(&self) -> &Resource {
41 &self.resource
42 }
43}
44
45impl std::str::FromStr for FuchsiaPkgComponentUrl {
46 type Err = ParseError;
47
48 fn from_str(url: &str) -> Result<Self, Self::Err> {
49 Self::parse(url)
50 }
51}
52
53impl std::convert::TryFrom<&str> for FuchsiaPkgComponentUrl {
54 type Error = ParseError;
55
56 fn try_from(value: &str) -> Result<Self, Self::Error> {
57 Self::parse(value)
58 }
59}
60
61impl std::fmt::Display for FuchsiaPkgComponentUrl {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(f, "{}#{}", self.package, self.resource.percent_encode())
64 }
65}
66
67impl serde::Serialize for FuchsiaPkgComponentUrl {
68 fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
69 self.to_string().serialize(ser)
70 }
71}
72
73impl<'de> serde::Deserialize<'de> for FuchsiaPkgComponentUrl {
74 fn deserialize<D>(de: D) -> Result<Self, D::Error>
75 where
76 D: serde::Deserializer<'de>,
77 {
78 let url = String::deserialize(de)?;
79 Ok(Self::parse(&url).map_err(|err| serde::de::Error::custom(err))?)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use assert_matches::assert_matches;
87 use std::convert::TryFrom as _;
88
89 #[test]
90 fn parse_err() {
91 for url in [
92 "fuchsia-boot://example.org/name#resource",
93 "fuchsia-pkg://name#resource",
94 "fuchsia-pkg://example.org#resource",
95 "fuchsia-pkg://example.org/#resource",
96 "fuchsia-pkg://example.org//#resource",
97 "fuchsia-pkg://example.org/name",
98 "fuchsia-pkg://exaMple.org/name#resource",
99 "fuchsia-pkg:///name#resource",
100 "fuchsia-pkg://#resource",
101 "example.org/name#resource",
102 "name/variant#resource",
103 "name",
104 "name?hash=0000000000000000000000000000000000000000000000000000000000000000",
105 "#resource",
106 ] {
107 assert_matches!(FuchsiaPkgComponentUrl::parse(url), Err(_), "the url {:?}", url);
108 assert_matches!(url.parse::<FuchsiaPkgComponentUrl>(), Err(_), "the url {:?}", url);
109 assert_matches!(FuchsiaPkgComponentUrl::try_from(url), Err(_), "the url {:?}", url);
110 assert_matches!(
111 serde_json::from_str::<FuchsiaPkgComponentUrl>(url),
112 Err(_),
113 "the url {:?}",
114 url
115 );
116 }
117 }
118
119 #[test]
120 fn parse_ok_absolute() {
121 for url in [
122 "fuchsia-pkg://example.org/name#resource",
123 "fuchsia-pkg://example.org/name#resource%09",
124 "fuchsia-pkg://example.org/name/variant#resource",
125 "fuchsia-pkg://example.org/name?hash=0000000000000000000000000000000000000000000000000000000000000000#resource",
126 "fuchsia-pkg://example.org/name/variant?hash=0000000000000000000000000000000000000000000000000000000000000000#resource",
127 ] {
128 let json_url = format!("\"{url}\"");
129 let validate = |parsed: &FuchsiaPkgComponentUrl| {
130 assert_eq!(parsed.to_string(), url);
131 assert_eq!(serde_json::to_string(&parsed).unwrap(), json_url);
132 };
133 validate(&FuchsiaPkgComponentUrl::parse(url).unwrap());
134 validate(&url.parse::<FuchsiaPkgComponentUrl>().unwrap());
135 validate(&FuchsiaPkgComponentUrl::try_from(url).unwrap());
136 validate(&serde_json::from_str::<FuchsiaPkgComponentUrl>(&json_url).unwrap());
137 }
138 }
139
140 #[test]
141 fn parse_ok_relative() {
142 for url in ["name#resource", "other-name#resource%09"] {
143 let json_url = format!("\"{url}\"");
144 let validate = |parsed: &FuchsiaPkgComponentUrl| {
145 assert_eq!(parsed.to_string(), url);
146 assert_eq!(serde_json::to_string(&parsed).unwrap(), json_url);
147 };
148 validate(&FuchsiaPkgComponentUrl::parse(url).unwrap());
149 validate(&url.parse::<FuchsiaPkgComponentUrl>().unwrap());
150 validate(&FuchsiaPkgComponentUrl::try_from(url).unwrap());
151 validate(&serde_json::from_str::<FuchsiaPkgComponentUrl>(&json_url).unwrap());
152 }
153 }
154}