1use std::fmt::Display as _;
6
7#[allow(private_bounds)]
11pub trait SchemeTrait: Sized + std::fmt::Display + std::clone::Clone + crate::Sealer {
12 fn try_from_part(scheme: crate::Scheme) -> Result<Self, crate::ParseError>;
13}
14
15#[allow(private_bounds)]
18pub trait HostTrait: Sized + std::fmt::Display + std::clone::Clone + crate::Sealer {
19 fn try_from_part(host: Option<crate::Host>) -> Result<Self, crate::ParseError>;
20}
21
22#[allow(private_bounds)]
26pub trait PathTrait: Sized + std::clone::Clone + crate::Sealer {
27 fn try_from_part(path: Option<crate::Path>) -> Result<Self, crate::ParseError>;
28 fn is_present(&self) -> bool;
29 fn url_display(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
30}
31
32#[allow(private_bounds)]
35pub trait HashTrait: Sized + std::clone::Clone + crate::Sealer {
36 fn try_from_part(hash: Option<crate::Hash>) -> Result<Self, crate::ParseError>;
37 fn is_present(&self) -> bool;
38 fn url_display(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
39}
40
41#[derive(Debug, Clone)]
44pub enum ComponentUrl<SCHEME, HOST, PATH, HASH> {
45 Absolute(AbsoluteComponentUrl<SCHEME, HOST, PATH, HASH>),
46 Relative(crate::RelativeComponentUrl),
47}
48
49impl<SCHEME, HOST, PATH, HASH> ComponentUrl<SCHEME, HOST, PATH, HASH>
50where
51 SCHEME: SchemeTrait,
52 HOST: HostTrait,
53 PATH: PathTrait,
54 HASH: HashTrait,
55{
56 pub fn parse(url: &str) -> Result<Self, crate::ParseError> {
58 let parts = crate::UrlParts::parse(url)?;
59 Ok(if parts.scheme.is_some() {
60 Self::Absolute(AbsoluteComponentUrl::try_from_parts(parts)?)
61 } else {
62 Self::Relative(crate::RelativeComponentUrl::from_parts(parts)?)
63 })
64 }
65
66 pub fn resource(&self) -> &crate::Resource {
68 match self {
69 Self::Absolute(absolute) => &absolute.resource,
70 Self::Relative(relative) => &relative.resource(),
71 }
72 }
73
74 pub fn to_package_url(&self) -> PackageUrl<SCHEME, HOST, PATH, HASH> {
76 match self {
77 Self::Absolute(absolute) => PackageUrl::Absolute(absolute.to_package_url()),
78 Self::Relative(relative) => PackageUrl::Relative(relative.package_url().clone()),
79 }
80 }
81}
82
83impl<SCHEME, HOST, PATH, HASH> std::fmt::Display for ComponentUrl<SCHEME, HOST, PATH, HASH>
84where
85 SCHEME: SchemeTrait,
86 HOST: HostTrait,
87 PATH: PathTrait,
88 HASH: HashTrait,
89{
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 match self {
92 Self::Absolute(absolute) => absolute.fmt(f),
93 Self::Relative(relative) => relative.fmt(f),
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
101pub struct AbsoluteComponentUrl<SCHEME, HOST, PATH, HASH> {
102 scheme: SCHEME,
103 host: HOST,
104 path: PATH,
105 hash: HASH,
106 resource: crate::Resource,
107}
108
109impl<SCHEME, HOST, PATH, HASH> AbsoluteComponentUrl<SCHEME, HOST, PATH, HASH>
110where
111 SCHEME: SchemeTrait,
112 HOST: HostTrait,
113 PATH: PathTrait,
114 HASH: HashTrait,
115{
116 pub fn parse(url: &str) -> Result<Self, crate::ParseError> {
118 let parts = crate::UrlParts::parse(url)?;
119 Self::try_from_parts(parts)
120 }
121
122 pub fn from_parts(
124 scheme: SCHEME,
125 host: HOST,
126 path: PATH,
127 hash: HASH,
128 resource: crate::Resource,
129 ) -> Self {
130 Self { scheme, host, path, hash, resource }
131 }
132
133 fn try_from_parts(parts: crate::UrlParts) -> Result<Self, crate::ParseError> {
134 let crate::UrlParts { scheme, host, path, hash, resource } = parts;
135 let scheme = SCHEME::try_from_part(scheme.ok_or(crate::ParseError::MissingScheme)?)?;
136 let host = HOST::try_from_part(host)?;
137 let path = PATH::try_from_part(path)?;
138 let hash = HASH::try_from_part(hash)?;
139 let Some(resource) = resource else {
140 return Err(crate::ParseError::MissingResource);
141 };
142 Ok(Self { scheme, host, path, hash, resource })
143 }
144
145 pub fn path(&self) -> &PATH {
147 &self.path
148 }
149
150 pub fn resource(&self) -> &crate::Resource {
152 &self.resource
153 }
154
155 pub fn to_package_url(&self) -> AbsolutePackageUrl<SCHEME, HOST, PATH, HASH> {
158 AbsolutePackageUrl::<SCHEME, HOST, PATH, HASH> {
159 scheme: self.scheme.clone(),
160 host: self.host.clone(),
161 path: self.path.clone(),
162 hash: self.hash.clone(),
163 }
164 }
165}
166
167impl<SCHEME, HOST, PATH, HASH> std::fmt::Display for AbsoluteComponentUrl<SCHEME, HOST, PATH, HASH>
168where
169 SCHEME: SchemeTrait,
170 HOST: HostTrait,
171 PATH: PathTrait,
172 HASH: HashTrait,
173{
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 let () = write!(f, "{}://{}", self.scheme, self.host)?;
176 if self.path.is_present() {
177 let () = f.write_str("/")?;
178 let () = self.path.url_display(f)?;
179 }
180 if self.hash.is_present() {
181 let () = f.write_str("?hash=")?;
182 let () = self.hash.url_display(f)?;
183 }
184 let () = write!(f, "#{}", self.resource)?;
185 Ok(())
186 }
187}
188
189pub enum PackageUrl<SCHEME, HOST, PATH, HASH> {
192 Absolute(AbsolutePackageUrl<SCHEME, HOST, PATH, HASH>),
193 Relative(crate::RelativePackageUrl),
194}
195
196impl<SCHEME, HOST, PATH, HASH> PackageUrl<SCHEME, HOST, PATH, HASH>
197where
198 SCHEME: SchemeTrait,
199 HOST: HostTrait,
200 PATH: PathTrait,
201 HASH: HashTrait,
202{
203 pub fn parse(url: &str) -> Result<Self, crate::ParseError> {
205 let parts = crate::UrlParts::parse(url)?;
206 Ok(if parts.scheme.is_some() {
207 Self::Absolute(AbsolutePackageUrl::try_from_parts(parts)?)
208 } else {
209 Self::Relative(crate::RelativePackageUrl::from_parts(parts)?)
210 })
211 }
212}
213
214impl<SCHEME, HOST, PATH, HASH> std::fmt::Display for PackageUrl<SCHEME, HOST, PATH, HASH>
215where
216 SCHEME: SchemeTrait,
217 HOST: HostTrait,
218 PATH: PathTrait,
219 HASH: HashTrait,
220{
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 match self {
223 Self::Absolute(absolute) => absolute.fmt(f),
224 Self::Relative(relative) => relative.fmt(f),
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
232pub struct AbsolutePackageUrl<SCHEME, HOST, PATH, HASH> {
233 scheme: SCHEME,
234 host: HOST,
235 path: PATH,
236 hash: HASH,
237}
238
239impl<SCHEME, HOST, PATH, HASH> AbsolutePackageUrl<SCHEME, HOST, PATH, HASH>
240where
241 SCHEME: SchemeTrait,
242 HOST: HostTrait,
243 PATH: PathTrait,
244 HASH: HashTrait,
245{
246 pub fn parse(url: &str) -> Result<Self, crate::ParseError> {
248 let parts = crate::UrlParts::parse(url)?;
249 Self::try_from_parts(parts)
250 }
251
252 fn try_from_parts(parts: crate::UrlParts) -> Result<Self, crate::ParseError> {
253 let crate::UrlParts { scheme, host, path, hash, resource } = parts;
254 let scheme = SCHEME::try_from_part(scheme.ok_or(crate::ParseError::MissingScheme)?)?;
255 let host = HOST::try_from_part(host)?;
256 let path = PATH::try_from_part(path)?;
257 let hash = HASH::try_from_part(hash)?;
258 if resource.is_some() {
259 return Err(crate::ParseError::CannotContainResource);
260 }
261 Ok(Self { scheme, host, path, hash })
262 }
263
264 pub fn path(&self) -> &PATH {
266 &self.path
267 }
268}
269
270impl<SCHEME, HOST, PATH, HASH> std::fmt::Display for AbsolutePackageUrl<SCHEME, HOST, PATH, HASH>
271where
272 SCHEME: SchemeTrait,
273 HOST: HostTrait,
274 PATH: PathTrait,
275 HASH: HashTrait,
276{
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 let () = write!(f, "{}://{}", self.scheme, self.host)?;
279 if self.path.is_present() {
280 let () = f.write_str("/")?;
281 let () = self.path.url_display(f)?;
282 }
283 if self.hash.is_present() {
284 let () = f.write_str("?hash=")?;
285 let () = self.hash.url_display(f)?;
286 }
287 Ok(())
288 }
289}
290
291impl crate::Sealer for crate::Scheme {}
293impl SchemeTrait for crate::Scheme {
294 fn try_from_part(scheme: crate::Scheme) -> Result<Self, crate::ParseError> {
295 Ok(scheme)
296 }
297}
298
299#[derive(Debug, Clone)]
301pub struct NoneHost;
302impl crate::Sealer for NoneHost {}
303impl HostTrait for NoneHost {
304 fn try_from_part(host: Option<crate::Host>) -> Result<Self, crate::ParseError> {
305 match host {
306 None => Ok(Self),
307 _ => Err(crate::ParseError::HostMustBeEmpty),
308 }
309 }
310}
311impl std::fmt::Display for NoneHost {
312 fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 Ok(())
314 }
315}
316
317#[derive(Debug, Clone)]
319pub struct OptionHost(Option<crate::Host>);
320impl crate::Sealer for OptionHost {}
321impl HostTrait for OptionHost {
322 fn try_from_part(host: Option<crate::Host>) -> Result<Self, crate::ParseError> {
323 Ok(Self(host))
324 }
325}
326impl std::fmt::Display for OptionHost {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 if let Some(host) = &self.0 { host.fmt(f) } else { Ok(()) }
329 }
330}
331
332impl crate::Sealer for Option<crate::Path> {}
333impl PathTrait for Option<crate::Path> {
334 fn try_from_part(path: Option<crate::Path>) -> Result<Self, crate::ParseError> {
335 Ok(path)
336 }
337 fn is_present(&self) -> bool {
338 self.is_some()
339 }
340 fn url_display(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 if let Some(path) = &self { path.fmt(f) } else { Ok(()) }
342 }
343}
344
345#[derive(Debug, Clone)]
347pub struct NoneHash;
348impl crate::Sealer for NoneHash {}
349impl HashTrait for NoneHash {
350 fn try_from_part(hash: Option<crate::Hash>) -> Result<Self, crate::ParseError> {
351 match hash {
352 None => Ok(Self),
353 _ => Err(crate::ParseError::CannotContainHash),
354 }
355 }
356 fn is_present(&self) -> bool {
357 false
358 }
359 fn url_display(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360 Ok(())
361 }
362}
363
364impl crate::Sealer for Option<crate::Hash> {}
365impl HashTrait for Option<crate::Hash> {
366 fn try_from_part(hash: Option<crate::Hash>) -> Result<Self, crate::ParseError> {
367 Ok(hash)
368 }
369 fn is_present(&self) -> bool {
370 self.is_some()
371 }
372 fn url_display(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373 if let Some(hash) = &self { hash.fmt(f) } else { Ok(()) }
374 }
375}