Coverage Report

Created: 2026-01-09 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/sfv-0.14.0/src/token.rs
Line
Count
Source
1
use std::{borrow::Borrow, fmt};
2
3
use crate::{
4
    error::{Error, NonEmptyStringError},
5
    utils,
6
};
7
8
/// An owned structured field value [token].
9
///
10
/// Tokens must match the following regular expression:
11
///
12
/// ```re
13
/// ^[A-Za-z*][A-Za-z*0-9!#$%&'+\-.^_`|~]*$
14
/// ```
15
///
16
/// [token]: <https://httpwg.org/specs/rfc9651.html#token>
17
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18
pub struct Token(String);
19
20
/// A borrowed structured field value [token].
21
///
22
/// Tokens must match the following regular expression:
23
///
24
/// ```re
25
/// ^[A-Za-z*][A-Za-z*0-9!#$%&'+\-.^_`|~]*$
26
/// ```
27
///
28
/// This type is to [`Token`] as [`str`] is to [`String`].
29
///
30
/// [token]: <https://httpwg.org/specs/rfc9651.html#token>
31
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, ref_cast::RefCastCustom)]
32
#[repr(transparent)]
33
pub struct TokenRef(str);
34
35
2.78M
const fn validate(v: &[u8]) -> Result<(), NonEmptyStringError> {
36
2.78M
    if v.is_empty() {
37
0
        return Err(NonEmptyStringError::empty());
38
2.78M
    }
39
40
2.78M
    if !utils::is_allowed_start_token_char(v[0]) {
41
0
        return Err(NonEmptyStringError::invalid_character(0));
42
2.78M
    }
43
44
2.78M
    let mut index = 1;
45
46
10.1M
    while index < v.len() {
47
7.34M
        if !utils::is_allowed_inner_token_char(v[index]) {
48
0
            return Err(NonEmptyStringError::invalid_character(index));
49
7.34M
        }
50
7.34M
        index += 1;
51
    }
52
53
2.78M
    Ok(())
54
2.78M
}
55
56
impl TokenRef {
57
    #[ref_cast::ref_cast_custom]
58
2.78M
    const fn cast(v: &str) -> &Self;
59
60
    /// Creates a `&TokenRef` from a `&str`.
61
    ///
62
    /// # Errors
63
    /// The error result reports the reason for any failed validation.
64
    #[allow(clippy::should_implement_trait)]
65
0
    pub fn from_str(v: &str) -> Result<&Self, Error> {
66
0
        validate(v.as_bytes())?;
67
0
        Ok(Self::cast(v))
68
0
    }
69
70
    // Like `from_str`, but assumes that the contents of the string have already
71
    // been validated as a token.
72
2.78M
    pub(crate) fn from_validated_str(v: &str) -> &Self {
73
2.78M
        debug_assert!(validate(v.as_bytes()).is_ok());
74
2.78M
        Self::cast(v)
75
2.78M
    }
76
77
    /// Creates a `&TokenRef`, panicking if the value is invalid.
78
    ///
79
    /// This method is intended to be called from `const` contexts in which the
80
    /// value is known to be valid. Use [`TokenRef::from_str`] for non-panicking
81
    /// conversions.
82
    #[must_use]
83
0
    pub const fn constant(v: &str) -> &Self {
84
0
        match validate(v.as_bytes()) {
85
0
            Ok(()) => Self::cast(v),
86
0
            Err(err) => panic!("{}", err.msg()),
87
        }
88
0
    }
89
90
    /// Returns the token as a `&str`.
91
    #[must_use]
92
0
    pub fn as_str(&self) -> &str {
93
0
        &self.0
94
0
    }
95
}
96
97
impl ToOwned for TokenRef {
98
    type Owned = Token;
99
100
2.78M
    fn to_owned(&self) -> Token {
101
2.78M
        Token(self.0.to_owned())
102
2.78M
    }
103
104
0
    fn clone_into(&self, target: &mut Token) {
105
0
        self.0.clone_into(&mut target.0);
106
0
    }
107
}
108
109
impl Borrow<TokenRef> for Token {
110
0
    fn borrow(&self) -> &TokenRef {
111
0
        self
112
0
    }
113
}
114
115
impl std::ops::Deref for Token {
116
    type Target = TokenRef;
117
118
0
    fn deref(&self) -> &TokenRef {
119
0
        TokenRef::cast(&self.0)
120
0
    }
121
}
122
123
impl From<Token> for String {
124
0
    fn from(v: Token) -> String {
125
0
        v.0
126
0
    }
127
}
128
129
impl TryFrom<String> for Token {
130
    type Error = Error;
131
132
0
    fn try_from(v: String) -> Result<Token, Error> {
133
0
        validate(v.as_bytes())?;
134
0
        Ok(Token(v))
135
0
    }
136
}
137
138
impl Token {
139
    /// Creates a `Token` from a `String`.
140
    ///
141
    /// Returns the original value if the conversion failed.
142
    ///
143
    /// # Errors
144
    /// The error result reports the reason for any failed validation.
145
0
    pub fn from_string(v: String) -> Result<Self, (Error, String)> {
146
0
        match validate(v.as_bytes()) {
147
0
            Ok(()) => Ok(Self(v)),
148
0
            Err(err) => Err((err.into(), v)),
149
        }
150
0
    }
151
}
152
153
/// Creates a `&TokenRef`, panicking if the value is invalid.
154
///
155
/// This is a convenience free function for [`TokenRef::constant`].
156
///
157
/// This method is intended to be called from `const` contexts in which the
158
/// value is known to be valid. Use [`TokenRef::from_str`] for non-panicking
159
/// conversions.
160
#[must_use]
161
0
pub const fn token_ref(v: &str) -> &TokenRef {
162
0
    TokenRef::constant(v)
163
0
}
164
165
impl fmt::Display for TokenRef {
166
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167
0
        f.write_str(self.as_str())
168
0
    }
169
}
170
171
impl fmt::Display for Token {
172
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173
0
        <TokenRef as fmt::Display>::fmt(self, f)
174
0
    }
175
}
176
177
macro_rules! impl_eq {
178
    ($a: ty, $b: ty) => {
179
        impl PartialEq<$a> for $b {
180
0
            fn eq(&self, other: &$a) -> bool {
181
0
                <TokenRef as PartialEq>::eq(self, other)
182
0
            }
Unexecuted instantiation: <sfv::token::TokenRef as core::cmp::PartialEq<sfv::token::Token>>::eq
Unexecuted instantiation: <&sfv::token::TokenRef as core::cmp::PartialEq<sfv::token::Token>>::eq
183
        }
184
        impl PartialEq<$b> for $a {
185
0
            fn eq(&self, other: &$b) -> bool {
186
0
                <TokenRef as PartialEq>::eq(self, other)
187
0
            }
Unexecuted instantiation: <sfv::token::Token as core::cmp::PartialEq<sfv::token::TokenRef>>::eq
Unexecuted instantiation: <sfv::token::Token as core::cmp::PartialEq<&sfv::token::TokenRef>>::eq
188
        }
189
    };
190
}
191
192
impl_eq!(Token, TokenRef);
193
impl_eq!(Token, &TokenRef);
194
195
impl<'a> TryFrom<&'a str> for &'a TokenRef {
196
    type Error = Error;
197
198
0
    fn try_from(v: &'a str) -> Result<&'a TokenRef, Error> {
199
0
        TokenRef::from_str(v)
200
0
    }
201
}
202
203
impl Borrow<str> for Token {
204
0
    fn borrow(&self) -> &str {
205
0
        self.as_str()
206
0
    }
207
}
208
209
impl Borrow<str> for TokenRef {
210
0
    fn borrow(&self) -> &str {
211
0
        self.as_str()
212
0
    }
213
}
214
215
#[cfg(feature = "arbitrary")]
216
impl<'a> arbitrary::Arbitrary<'a> for &'a TokenRef {
217
    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
218
        TokenRef::from_str(<&str>::arbitrary(u)?).map_err(|_| arbitrary::Error::IncorrectFormat)
219
    }
220
}
221
222
#[cfg(feature = "arbitrary")]
223
impl<'a> arbitrary::Arbitrary<'a> for Token {
224
    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
225
        <&TokenRef>::arbitrary(u).map(ToOwned::to_owned)
226
    }
227
}