webparse/url/
scheme.rs

1// Copyright 2022 - 2023 Wenmeng See the COPYRIGHT
2// file at the top-level directory of this distribution.
3// 
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8// 
9// Author: tickbh
10// -----
11// Created Date: 2023/08/16 09:53:49
12
13use std::{fmt::Display, str::FromStr};
14
15use algorithm::buf::{Bt, BtMut};
16use crate::{byte_map, Helper, HttpError, Serialize, WebError, WebResult};
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash)]
19pub enum Scheme {
20    None,
21    Http,
22    Https,
23    Ws,
24    Wss,
25    Ftp,
26    Extension(String),
27}
28
29impl Scheme {
30    const MAX_SCHEME_LEN: usize = 64;
31    // ASCII codes to accept URI string.
32    // i.e. A-Z a-z 0-9 !#$%&'*+-._();:@=,/?[]~^
33    // TODO: Make a stricter checking for URI string?
34    const SCHEME_MAP: [bool; 256] = byte_map![
35        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36        //  \0                            \n
37        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //  commands
38        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0,
39        //  \w !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /
40        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
41        //  0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
42        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
43        //  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O
44        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
45        //  P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^  _
46        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
47        //  `  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o
48        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
49        //  p  q  r  s  t  u  v  w  x  y  z  {  |  }  ~  del
50        //   ====== Extended ASCII (aka. obs-text) ======
51        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55        0, 0, 0, 0, 0, 0, 0,
56    ];
57
58    #[inline]
59    pub(crate) fn is_scheme_token(b: u8) -> bool {
60        Self::SCHEME_MAP[b as usize]
61    }
62
63    pub fn parse_scheme<T: Bt>(buffer: &mut T) -> WebResult<Scheme> {
64        let scheme = Helper::parse_scheme(buffer)?;
65        if scheme.as_bytes().len() > Self::MAX_SCHEME_LEN {
66            return Err(WebError::Http(HttpError::SchemeTooLong));
67        }
68        Scheme::try_from(scheme)
69    }
70
71    pub fn as_str(&self) -> &str {
72        match self {
73            Scheme::Http => "http",
74            Scheme::Https => "https",
75            Scheme::Ws => "ws",
76            Scheme::Wss => "wss",
77            Scheme::Ftp => "ftp",
78            Scheme::Extension(s) => &s.as_str(),
79            Scheme::None => "",
80        }
81    }
82
83    pub fn is_none(&self) -> bool {
84        match self {
85            Scheme::None => true,
86            _ => false,
87        }
88    }
89
90    pub fn is_http(&self) -> bool {
91        match self {
92            Scheme::Http => true,
93            _ => false,
94        }
95    }
96
97    pub fn is_https(&self) -> bool {
98        match self {
99            Scheme::Https => true,
100            _ => false,
101        }
102    }
103
104
105    pub fn is_ws(&self) -> bool {
106        match self {
107            Scheme::Ws => true,
108            _ => false,
109        }
110    }
111
112    pub fn is_wss(&self) -> bool {
113        match self {
114            Scheme::Wss => true,
115            _ => false,
116        }
117    }
118}
119
120impl Display for Scheme {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        f.write_str(&self.as_str())
123    }
124}
125
126impl Serialize for Scheme {
127    fn serialize<B: Bt + BtMut>(&mut self, buffer: &mut B) -> WebResult<usize> {
128        match self {
129            Scheme::None => Err(WebError::Serialize("scheme")),
130            _ => Ok(buffer.put_slice(self.as_str().as_bytes())),
131        }
132    }
133}
134
135impl TryFrom<&str> for Scheme {
136    type Error = WebError;
137
138    fn try_from(value: &str) -> Result<Self, Self::Error> {
139        if value.len() > 64 {
140            return Err(WebError::from(crate::UrlError::UrlInvalid));
141        }
142        match value {
143            "http" => Ok(Scheme::Http),
144            "https" => Ok(Scheme::Https),
145            "ws" => Ok(Scheme::Ws),
146            "wss" => Ok(Scheme::Wss),
147            "ftp" => Ok(Scheme::Ftp),
148            _ => Ok(Scheme::Extension(value.to_string())),
149        }
150    }
151}
152
153impl FromStr for Scheme {
154    type Err = WebError;
155    fn from_str(s: &str) -> Result<Self, Self::Err> {
156        Scheme::try_from(&*s.to_uppercase())
157    }
158}