1use std::cmp::Ordering;
4use std::str::FromStr;
5use std::{fmt, iter};
6
7use derive_more::*;
8use get_size::GetSize;
9use get_size_derive::*;
10use smallvec::SmallVec;
11
12pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
13
14pub use hr_id::{label, Id, Label, ParseError};
15
16mod path;
17#[cfg(feature = "serialize")]
18mod serial;
19#[cfg(feature = "stream")]
20mod stream;
21
22pub use path::*;
23
24pub type Port = u16;
26
27type Segments<T> = SmallVec<[T; 8]>;
28
29pub enum ToUrl<'a> {
31 Link(Link),
32 LinkRef(&'a Link),
33 Path(PathBuf),
34 PathRef(&'a [PathSegment]),
35}
36
37impl<'a> ToUrl<'a> {
38 pub fn to_link(&self) -> Link {
40 match self {
41 Self::Link(link) => (*link).clone(),
42 Self::LinkRef(link) => (**link).clone(),
43 Self::Path(path) => path.clone().into(),
44 Self::PathRef(path) => PathBuf::from_slice(path).into(),
45 }
46 }
47
48 pub fn host(&self) -> Option<&Host> {
50 match self {
51 Self::Link(link) => link.host(),
52 Self::LinkRef(link) => link.host(),
53 _ => None,
54 }
55 }
56
57 pub fn path(&self) -> &[PathSegment] {
59 match self {
60 Self::Link(link) => link.path(),
61 Self::LinkRef(link) => link.path(),
62 Self::Path(path) => path,
63 Self::PathRef(path) => path,
64 }
65 }
66
67 pub fn parse<Url>(&self) -> Result<Url, <Url as FromStr>::Err>
69 where
70 Url: FromStr,
71 {
72 self.to_string().parse()
73 }
74}
75
76impl<'a> fmt::Display for ToUrl<'a> {
77 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 match self {
79 Self::Link(link) => fmt::Display::fmt(link, f),
80 Self::LinkRef(link) => fmt::Display::fmt(link, f),
81 Self::Path(path) => fmt::Display::fmt(path, f),
82 Self::PathRef(path) => {
83 if path.is_empty() {
84 f.write_str("/")?;
85 }
86
87 for segment in path.iter() {
88 write!(f, "/{segment}")?;
89 }
90
91 Ok(())
92 }
93 }
94 }
95}
96
97impl<'a> From<Link> for ToUrl<'a> {
98 fn from(link: Link) -> Self {
99 Self::Link(link)
100 }
101}
102
103impl<'a> From<&'a Link> for ToUrl<'a> {
104 fn from(link: &'a Link) -> Self {
105 Self::LinkRef(link)
106 }
107}
108
109impl<'a> From<PathBuf> for ToUrl<'a> {
110 fn from(path: PathBuf) -> Self {
111 Self::Path(path)
112 }
113}
114
115impl<'a> From<&'a [PathSegment]> for ToUrl<'a> {
116 fn from(path: &'a [PathSegment]) -> Self {
117 Self::PathRef(path.into())
118 }
119}
120
121#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, GetSize)]
123pub enum Protocol {
124 HTTP,
125}
126
127impl PartialOrd for Protocol {
128 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
129 Some(self.cmp(other))
130 }
131}
132
133impl Ord for Protocol {
134 fn cmp(&self, other: &Self) -> Ordering {
135 match (self, other) {
136 (Self::HTTP, Self::HTTP) => Ordering::Equal,
137 }
138 }
139}
140
141impl Default for Protocol {
142 fn default() -> Protocol {
143 Protocol::HTTP
144 }
145}
146
147impl fmt::Display for Protocol {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 f.write_str(match self {
150 Self::HTTP => "http",
151 })
152 }
153}
154
155#[derive(Clone, Debug, Display, Eq, PartialEq, Hash)]
157pub enum Address {
158 IPv4(Ipv4Addr),
159 IPv6(Ipv6Addr),
160 }
162
163impl Default for Address {
164 fn default() -> Self {
165 Self::LOCALHOST
166 }
167}
168
169impl Address {
170 pub const LOCALHOST: Self = Self::IPv4(Ipv4Addr::LOCALHOST);
171
172 pub fn as_ip(&self) -> Option<IpAddr> {
174 match self {
175 Self::IPv4(addr) => Some((*addr).into()),
176 Self::IPv6(addr) => Some((*addr).into()),
177 }
178 }
179
180 pub fn is_localhost(&self) -> bool {
182 match self {
183 Self::IPv4(addr) => addr.is_loopback(),
184 Self::IPv6(addr) => addr.is_loopback(),
185 }
186 }
187}
188
189impl PartialOrd for Address {
190 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
191 Some(self.cmp(other))
192 }
193}
194
195impl Ord for Address {
196 fn cmp(&self, other: &Self) -> Ordering {
197 match (self, other) {
198 (Self::IPv4(this), Self::IPv4(that)) => this.cmp(that),
199 (Self::IPv6(this), Self::IPv6(that)) => this.cmp(that),
200 (Self::IPv4(_this), _) => Ordering::Less,
201 (Self::IPv6(_this), _) => Ordering::Greater,
202 }
203 }
204}
205
206impl GetSize for Address {
207 fn get_size(&self) -> usize {
208 match self {
209 Self::IPv4(_) => 4,
210 Self::IPv6(_) => 16,
211 }
212 }
213}
214
215impl From<Ipv4Addr> for Address {
216 fn from(addr: Ipv4Addr) -> Address {
217 Self::IPv4(addr)
218 }
219}
220
221impl From<Ipv6Addr> for Address {
222 fn from(addr: Ipv6Addr) -> Address {
223 Self::IPv6(addr)
224 }
225}
226
227impl From<IpAddr> for Address {
228 fn from(addr: IpAddr) -> Address {
229 match addr {
230 IpAddr::V4(addr) => Self::IPv4(addr),
231 IpAddr::V6(addr) => Self::IPv6(addr),
232 }
233 }
234}
235
236impl PartialEq<Ipv4Addr> for Address {
237 fn eq(&self, other: &Ipv4Addr) -> bool {
238 match self {
239 Self::IPv4(addr) => addr == other,
240 _ => false,
241 }
242 }
243}
244
245impl PartialEq<Ipv6Addr> for Address {
246 fn eq(&self, other: &Ipv6Addr) -> bool {
247 match self {
248 Self::IPv6(addr) => addr == other,
249 _ => false,
250 }
251 }
252}
253
254impl PartialEq<IpAddr> for Address {
255 fn eq(&self, other: &IpAddr) -> bool {
256 use IpAddr::*;
257
258 match other {
259 V4(addr) => self == addr,
260 V6(addr) => self == addr,
261 }
262 }
263}
264
265#[derive(Clone, Debug, Hash, Eq, PartialEq, GetSize)]
267pub struct Host {
268 protocol: Protocol,
269 address: Address,
270 port: Option<Port>,
271}
272
273impl Host {
274 pub fn is_localhost(&self) -> bool {
276 self.address.is_localhost()
277 }
278
279 pub fn is_loopback(&self, public_addr: Option<&Host>) -> bool {
281 if let Some(addr) = public_addr {
282 self == addr || (self.is_localhost() && self.port == addr.port)
283 } else {
284 self.is_localhost()
285 }
286 }
287
288 pub fn protocol(&self) -> Protocol {
290 self.protocol
291 }
292
293 pub fn address(&self) -> &Address {
295 &self.address
296 }
297
298 pub fn port(&self) -> Option<Port> {
300 self.port
301 }
302}
303
304impl FromStr for Host {
305 type Err = ParseError;
306
307 fn from_str(s: &str) -> Result<Host, ParseError> {
308 if !s.starts_with("http://") {
309 return Err(format!("invalid protocol: {}", s).into());
310 }
311
312 let protocol = Protocol::HTTP;
313
314 let s = &s[7..];
315
316 let (address, port): (Address, Option<u16>) = if s.contains("::") {
317 let mut segments: Segments<&str> = s.split("::").collect();
318 let port: Option<u16> = if segments.last().unwrap().contains(':') {
319 let last_segment: Segments<&str> = segments.pop().unwrap().split(':').collect();
320 if last_segment.len() == 2 {
321 segments.push(last_segment[0]);
322
323 let port = last_segment[1].parse().map_err(|cause| {
324 format!("{} is not a valid port number: {}", last_segment[1], cause)
325 })?;
326
327 Some(port)
328 } else {
329 return Err(format!("invalid IPv6 address: {}", s).into());
330 }
331 } else {
332 None
333 };
334
335 let address = segments.join("::");
336 let address: Ipv6Addr = address.parse().map_err(|cause| {
337 ParseError::from(format!(
338 "{} is not a valid IPv6 address: {}",
339 address, cause
340 ))
341 })?;
342
343 (address.into(), port)
344 } else {
345 let (address, port) = if s.contains(':') {
346 let segments: Segments<&str> = s.split(':').collect();
347 if segments.len() == 2 {
348 let port: u16 = segments[1].parse().map_err(|cause| {
349 ParseError::from(format!(
350 "{} is not a valid port number: {}",
351 segments[1], cause
352 ))
353 })?;
354
355 (segments[0], Some(port))
356 } else {
357 return Err(format!("invalid network address: {}", s).into());
358 }
359 } else {
360 (s, None)
361 };
362
363 let address: Ipv4Addr = address.parse().map_err(|cause| {
364 ParseError::from(format!(
365 "{} is not a valid IPv4 address: {}",
366 address, cause
367 ))
368 })?;
369
370 (address.into(), port)
371 };
372
373 Ok(Host {
374 protocol,
375 address,
376 port,
377 })
378 }
379}
380
381impl PartialOrd for Host {
382 fn partial_cmp(&self, other: &Host) -> Option<Ordering> {
383 Some(self.cmp(other))
384 }
385}
386
387impl Ord for Host {
388 fn cmp(&self, other: &Self) -> Ordering {
389 match self.protocol.cmp(&other.protocol) {
390 Ordering::Equal => match self.address.cmp(&other.address) {
391 Ordering::Equal => self.port.cmp(&other.port),
392 ordering => ordering,
393 },
394 ordering => ordering,
395 }
396 }
397}
398
399impl From<(Protocol, Address)> for Host {
400 fn from(components: (Protocol, Address)) -> Self {
401 let (protocol, address) = components;
402 Self {
403 protocol,
404 address,
405 port: None,
406 }
407 }
408}
409
410impl From<(Address, Port)> for Host {
411 fn from(components: (Address, Port)) -> Self {
412 let (address, port) = components;
413 Self {
414 protocol: Protocol::default(),
415 address,
416 port: Some(port),
417 }
418 }
419}
420
421impl From<(Protocol, Address, Port)> for Host {
422 fn from(components: (Protocol, Address, Port)) -> Self {
423 let (protocol, address, port) = components;
424 Self {
425 protocol,
426 address,
427 port: Some(port),
428 }
429 }
430}
431
432impl From<(Protocol, Address, Option<Port>)> for Host {
433 fn from(components: (Protocol, Address, Option<Port>)) -> Self {
434 let (protocol, address, port) = components;
435 Self {
436 protocol,
437 address,
438 port,
439 }
440 }
441}
442
443impl fmt::Display for Host {
444 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
445 if let Some(port) = self.port {
446 write!(f, "{}://{}:{}", self.protocol, self.address, port)
447 } else {
448 write!(f, "{}://{}", self.protocol, self.address)
449 }
450 }
451}
452
453#[derive(Clone, Default, Eq, Hash, PartialEq, GetSize)]
455pub struct Link {
456 host: Option<Host>,
457 path: PathBuf,
458}
459
460impl Link {
461 pub fn new(host: Host, path: PathBuf) -> Self {
463 Self {
464 host: Some(host),
465 path,
466 }
467 }
468
469 pub fn into_inner(self) -> (Option<Host>, PathBuf) {
471 (self.host, self.path)
472 }
473
474 pub fn into_host(self) -> Option<Host> {
476 self.host
477 }
478
479 pub fn into_path(self) -> PathBuf {
481 self.path
482 }
483
484 pub fn host(&self) -> Option<&Host> {
486 self.host.as_ref()
487 }
488
489 pub fn path(&self) -> &PathBuf {
491 &self.path
492 }
493
494 pub fn path_mut(&mut self) -> &mut PathBuf {
496 &mut self.path
497 }
498
499 pub fn append<S: Into<PathSegment>>(mut self, segment: S) -> Self {
501 self.path = self.path.append(segment);
502 self
503 }
504}
505
506impl Extend<PathSegment> for Link {
507 fn extend<I: IntoIterator<Item = PathSegment>>(&mut self, iter: I) {
508 self.path.extend(iter)
509 }
510}
511
512impl PartialEq<[PathSegment]> for Link {
513 fn eq(&self, other: &[PathSegment]) -> bool {
514 if self.host.is_some() {
515 return false;
516 }
517
518 &self.path == other
519 }
520}
521
522impl PartialEq<String> for Link {
523 fn eq(&self, other: &String) -> bool {
524 self == other.as_str()
525 }
526}
527
528impl PartialEq<str> for Link {
529 fn eq(&self, other: &str) -> bool {
530 if other.is_empty() {
531 false
532 } else if other.starts_with('/') {
533 self.host.is_none() && &self.path == other
534 } else if other.ends_with('/') {
535 self.to_string() == other[..other.len() - 1]
536 } else {
537 self.to_string() == other
538 }
539 }
540}
541
542impl From<Host> for Link {
543 fn from(host: Host) -> Link {
544 Link {
545 host: Some(host),
546 path: PathBuf::default(),
547 }
548 }
549}
550
551impl From<PathLabel> for Link {
552 fn from(path: PathLabel) -> Self {
553 PathBuf::from(path).into()
554 }
555}
556
557impl From<PathBuf> for Link {
558 fn from(path: PathBuf) -> Link {
559 Link { host: None, path }
560 }
561}
562
563impl From<(Host, PathBuf)> for Link {
564 fn from(tuple: (Host, PathBuf)) -> Link {
565 let (host, path) = tuple;
566 Link {
567 host: Some(host),
568 path,
569 }
570 }
571}
572
573impl From<(Option<Host>, PathBuf)> for Link {
574 fn from(tuple: (Option<Host>, PathBuf)) -> Link {
575 let (host, path) = tuple;
576 Link { host, path }
577 }
578}
579
580impl FromStr for Link {
581 type Err = ParseError;
582
583 fn from_str(s: &str) -> Result<Link, ParseError> {
584 if s.starts_with('/') {
585 return Ok(Link {
586 host: None,
587 path: s.parse()?,
588 });
589 } else if !s.starts_with("http://") {
590 return Err(format!("cannot parse {} as a Link: invalid protocol", s).into());
591 }
592
593 let s = if s.ends_with('/') {
594 &s[..s.len() - 1]
595 } else {
596 s
597 };
598
599 let segments: Segments<&str> = s.split('/').collect();
600
601 if segments.is_empty() {
602 return Err(format!("invalid Link: {}", s).into());
603 }
604
605 let host: Host = segments[..3].join("/").parse()?;
606
607 let segments = &segments[3..];
608
609 let segments = segments
610 .iter()
611 .map(|s| s.parse())
612 .collect::<Result<Segments<PathSegment>, ParseError>>()?;
613
614 Ok(Link {
615 host: Some(host),
616 path: iter::FromIterator::from_iter(segments),
617 })
618 }
619}
620
621impl PartialOrd for Link {
622 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
623 Some(self.cmp(other))
624 }
625}
626
627impl Ord for Link {
628 fn cmp(&self, other: &Self) -> Ordering {
629 match (&self.host, &other.host) {
630 (None, None) => self.path.cmp(&other.path),
631 (Some(this), Some(that)) => match this.cmp(&that) {
632 Ordering::Equal => self.path.cmp(&other.path),
633 ordering => ordering,
634 },
635 (Some(_), _) => Ordering::Less,
636 (_, Some(_)) => Ordering::Greater,
637 }
638 }
639}
640
641impl TryFrom<Link> for PathBuf {
642 type Error = ParseError;
643
644 fn try_from(link: Link) -> Result<Self, Self::Error> {
645 if link.host.is_none() {
646 Ok(link.path)
647 } else {
648 Err(ParseError::from(format!(
649 "expected a path but found {}",
650 link
651 )))
652 }
653 }
654}
655
656impl fmt::Debug for Link {
657 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
658 if let Some(host) = &self.host {
659 write!(f, "{:?}{:?}", host, self.path)
660 } else {
661 fmt::Debug::fmt(&self.path, f)
662 }
663 }
664}
665
666impl fmt::Display for Link {
667 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
668 if let Some(host) = &self.host {
669 write!(f, "{}{}", host, self.path)
670 } else {
671 fmt::Display::fmt(&self.path, f)
672 }
673 }
674}
675
676#[cfg(feature = "hash")]
677impl<D: async_hash::Digest> async_hash::Hash<D> for Link {
678 fn hash(self) -> async_hash::Output<D> {
679 async_hash::Hash::<D>::hash(&self)
680 }
681}
682
683#[cfg(feature = "hash")]
684impl<'a, D: async_hash::Digest> async_hash::Hash<D> for &'a Link {
685 fn hash(self) -> async_hash::Output<D> {
686 if self == &Link::default() {
687 async_hash::default_hash::<D>()
688 } else {
689 async_hash::Hash::<D>::hash(self.to_string())
690 }
691 }
692}