1use std::{
2 fmt::{Debug, Display},
3 str::FromStr,
4};
5
6use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS};
7
8const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
10
11#[derive(Clone, PartialEq, Eq)]
20pub enum Url {
21 Fragment(String),
23 Other(String),
25}
26
27impl FromStr for Url {
28 type Err = std::str::Utf8Error;
29
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 let mut it = s.chars();
32 Ok(if it.next() == Some('#') {
33 Url::Fragment(percent_decode_str(it.as_str()).decode_utf8()?.into())
34 } else {
35 Url::Other(s.into())
36 })
37 }
38}
39
40impl Display for Url {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 Self::Fragment(s) => write!(f, "#{}", percent_encode(s.as_bytes(), FRAGMENT)),
44 Self::Other(s) => write!(f, "{}", s),
45 }
46 }
47}
48
49impl Debug for Url {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 Display::fmt(self, f)
52 }
53}