1use crate::errors::ParseError;
6use crate::parse::{PackageName, PackageVariant};
7use crate::{FuchsiaPkgAbsolutePackageUrl, FuchsiaPkgUnpinnedAbsolutePackageUrl, RepositoryUrl};
8use fuchsia_hash::Hash;
9
10#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct FuchsiaPkgPinnedAbsolutePackageUrl {
19 unpinned: FuchsiaPkgUnpinnedAbsolutePackageUrl,
20 hash: Hash,
21}
22
23impl FuchsiaPkgPinnedAbsolutePackageUrl {
24 pub fn new(
26 repo: RepositoryUrl,
27 name: PackageName,
28 variant: Option<PackageVariant>,
29 hash: Hash,
30 ) -> Self {
31 Self { unpinned: FuchsiaPkgUnpinnedAbsolutePackageUrl::new(repo, name, variant), hash }
32 }
33
34 pub fn new_with_path(repo: RepositoryUrl, path: &str, hash: Hash) -> Result<Self, ParseError> {
37 Ok(Self {
38 unpinned: FuchsiaPkgUnpinnedAbsolutePackageUrl::new_with_path(repo, path)?,
39 hash,
40 })
41 }
42
43 pub fn parse(url: &str) -> Result<Self, ParseError> {
45 match FuchsiaPkgAbsolutePackageUrl::parse(url)? {
46 FuchsiaPkgAbsolutePackageUrl::Unpinned(_) => Err(ParseError::MissingHash),
47 FuchsiaPkgAbsolutePackageUrl::Pinned(pinned) => Ok(pinned),
48 }
49 }
50
51 pub fn from_unpinned(unpinned: FuchsiaPkgUnpinnedAbsolutePackageUrl, hash: Hash) -> Self {
53 Self { unpinned, hash }
54 }
55
56 pub fn into_unpinned_and_hash(self) -> (FuchsiaPkgUnpinnedAbsolutePackageUrl, Hash) {
58 let Self { unpinned, hash } = self;
59 (unpinned, hash)
60 }
61
62 pub fn as_unpinned(&self) -> &FuchsiaPkgUnpinnedAbsolutePackageUrl {
64 &self.unpinned
65 }
66
67 pub fn hash(&self) -> Hash {
69 self.hash
70 }
71
72 pub fn set_repository(&mut self, repository: RepositoryUrl) -> &mut Self {
74 self.unpinned.set_repository(repository);
75 self
76 }
77}
78
79impl std::ops::Deref for FuchsiaPkgPinnedAbsolutePackageUrl {
82 type Target = FuchsiaPkgUnpinnedAbsolutePackageUrl;
83
84 fn deref(&self) -> &Self::Target {
85 &self.unpinned
86 }
87}
88
89impl std::ops::DerefMut for FuchsiaPkgPinnedAbsolutePackageUrl {
92 fn deref_mut(&mut self) -> &mut Self::Target {
93 &mut self.unpinned
94 }
95}
96
97impl std::str::FromStr for FuchsiaPkgPinnedAbsolutePackageUrl {
98 type Err = ParseError;
99
100 fn from_str(url: &str) -> Result<Self, Self::Err> {
101 Self::parse(url)
102 }
103}
104
105impl std::convert::TryFrom<&str> for FuchsiaPkgPinnedAbsolutePackageUrl {
106 type Error = ParseError;
107
108 fn try_from(value: &str) -> Result<Self, Self::Error> {
109 Self::parse(value)
110 }
111}
112
113impl std::fmt::Display for FuchsiaPkgPinnedAbsolutePackageUrl {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 write!(f, "{}?hash={}", self.unpinned, self.hash)
116 }
117}
118
119impl serde::Serialize for FuchsiaPkgPinnedAbsolutePackageUrl {
120 fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
121 self.to_string().serialize(ser)
122 }
123}
124
125impl<'de> serde::Deserialize<'de> for FuchsiaPkgPinnedAbsolutePackageUrl {
126 fn deserialize<D>(de: D) -> Result<Self, D::Error>
127 where
128 D: serde::Deserializer<'de>,
129 {
130 let url = String::deserialize(de)?;
131 Ok(Self::parse(&url).map_err(|err| serde::de::Error::custom(err))?)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::errors::PackagePathSegmentError;
139 use assert_matches::assert_matches;
140 use std::convert::TryFrom as _;
141
142 #[test]
143 fn parse_err() {
144 for (url, err) in [
145 (
146 "fuchsia-boot://example.org/name?hash=0000000000000000000000000000000000000000000000000000000000000000",
147 ParseError::InvalidScheme,
148 ),
149 (
150 "fuchsia-pkg://?hash=0000000000000000000000000000000000000000000000000000000000000000",
151 ParseError::MissingHost,
152 ),
153 (
154 "fuchsia-pkg://exaMple.org?hash=0000000000000000000000000000000000000000000000000000000000000000",
155 ParseError::InvalidHost,
156 ),
157 (
158 "fuchsia-pkg://example.org/?hash=0000000000000000000000000000000000000000000000000000000000000000",
159 ParseError::MissingName,
160 ),
161 (
162 "fuchsia-pkg://example.org//?hash=0000000000000000000000000000000000000000000000000000000000000000",
163 ParseError::InvalidPathSegment(PackagePathSegmentError::Empty),
164 ),
165 (
166 "fuchsia-pkg://example.org/name/variant/extra?hash=0000000000000000000000000000000000000000000000000000000000000000",
167 ParseError::ExtraPathSegments,
168 ),
169 (
170 "fuchsia-pkg://example.org/name?hash=0000000000000000000000000000000000000000000000000000000000000000#resource",
171 ParseError::CannotContainResource,
172 ),
173 ] {
174 assert_matches!(
175 FuchsiaPkgPinnedAbsolutePackageUrl::parse(url),
176 Err(e) if e == err,
177 "the url {:?}", url
178 );
179 assert_matches!(
180 url.parse::<FuchsiaPkgPinnedAbsolutePackageUrl>(),
181 Err(e) if e == err,
182 "the url {:?}", url
183 );
184 assert_matches!(
185 FuchsiaPkgPinnedAbsolutePackageUrl::try_from(url),
186 Err(e) if e == err,
187 "the url {:?}", url
188 );
189 assert_matches!(
190 serde_json::from_str::<FuchsiaPkgPinnedAbsolutePackageUrl>(url),
191 Err(_),
192 "the url {:?}",
193 url
194 );
195 }
196 }
197
198 #[test]
199 fn parse_ok() {
200 for (url, variant, path) in [
201 (
202 "fuchsia-pkg://example.org/name?hash=0000000000000000000000000000000000000000000000000000000000000000",
203 None,
204 "name",
205 ),
206 (
207 "fuchsia-pkg://example.org/name/variant?hash=0000000000000000000000000000000000000000000000000000000000000000",
208 Some("variant"),
209 "name/variant",
210 ),
211 ] {
212 let json_url = format!("\"{url}\"");
213 let host = "example.org";
214 let name = "name";
215 let hash = "0000000000000000000000000000000000000000000000000000000000000000"
216 .parse::<Hash>()
217 .unwrap();
218
219 let name = name.parse::<crate::PackageName>().unwrap();
221 let variant = variant.map(|v| v.parse::<crate::PackageVariant>().unwrap());
222 let validate = |parsed: &FuchsiaPkgPinnedAbsolutePackageUrl| {
223 assert_eq!(parsed.host(), host);
224 assert_eq!(parsed.name(), &name);
225 assert_eq!(parsed.variant(), variant.as_ref());
226 assert_eq!(parsed.path(), path);
227 assert_eq!(parsed.hash(), hash);
228 };
229 validate(&FuchsiaPkgPinnedAbsolutePackageUrl::parse(url).unwrap());
230 validate(&url.parse::<FuchsiaPkgPinnedAbsolutePackageUrl>().unwrap());
231 validate(&FuchsiaPkgPinnedAbsolutePackageUrl::try_from(url).unwrap());
232 validate(
233 &serde_json::from_str::<FuchsiaPkgPinnedAbsolutePackageUrl>(&json_url).unwrap(),
234 );
235
236 assert_eq!(
238 FuchsiaPkgPinnedAbsolutePackageUrl::parse(url).unwrap().to_string(),
239 url,
240 "the url {:?}",
241 url
242 );
243 assert_eq!(
244 serde_json::to_string(&FuchsiaPkgPinnedAbsolutePackageUrl::parse(url).unwrap())
245 .unwrap(),
246 json_url,
247 "the url {:?}",
248 url
249 );
250 }
251 }
252}