/rust/registry/src/index.crates.io-1949cf8c6b5b557f/password-hash-0.4.2/src/ident.rs
Line | Count | Source |
1 | | //! Algorithm or parameter identifier. |
2 | | //! |
3 | | //! Implements the following parts of the [PHC string format specification][1]: |
4 | | //! |
5 | | //! > The function symbolic name is a sequence of characters in: `[a-z0-9-]` |
6 | | //! > (lowercase letters, digits, and the minus sign). No other character is |
7 | | //! > allowed. Each function defines its own identifier (or identifiers in case |
8 | | //! > of a function family); identifiers should be explicit (human readable, |
9 | | //! > not a single digit), with a length of about 5 to 10 characters. An |
10 | | //! > identifier name MUST NOT exceed 32 characters in length. |
11 | | //! > |
12 | | //! > Each parameter name shall be a sequence of characters in: `[a-z0-9-]` |
13 | | //! > (lowercase letters, digits, and the minus sign). No other character is |
14 | | //! > allowed. Parameter names SHOULD be readable for a human user. A |
15 | | //! > parameter name MUST NOT exceed 32 characters in length. |
16 | | //! |
17 | | //! [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md |
18 | | |
19 | | use crate::{Error, Result}; |
20 | | use core::{fmt, ops::Deref, str}; |
21 | | |
22 | | /// Algorithm or parameter identifier. |
23 | | /// |
24 | | /// This type encompasses both the "function symbolic name" and "parameter name" |
25 | | /// use cases as described in the [PHC string format specification][1]. |
26 | | /// |
27 | | /// # Constraints |
28 | | /// - ASCII-encoded string consisting of the characters `[a-z0-9-]` |
29 | | /// (lowercase letters, digits, and the minus sign) |
30 | | /// - Minimum length: 1 ASCII character (i.e. 1-byte) |
31 | | /// - Maximum length: 32 ASCII characters (i.e. 32-bytes) |
32 | | /// |
33 | | /// [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md |
34 | | #[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] |
35 | | pub struct Ident<'a>(&'a str); |
36 | | |
37 | | impl<'a> Ident<'a> { |
38 | | /// Maximum length of an [`Ident`] - 32 ASCII characters (i.e. 32-bytes). |
39 | | /// |
40 | | /// This value corresponds to the maximum size of a function symbolic names |
41 | | /// and parameter names according to the PHC string format. |
42 | | /// Maximum length of an [`Ident`] - 32 ASCII characters (i.e. 32-bytes). |
43 | | /// |
44 | | /// This value corresponds to the maximum size of a function symbolic names |
45 | | /// and parameter names according to the PHC string format. |
46 | | const MAX_LENGTH: usize = 32; |
47 | | |
48 | | /// Parse an [`Ident`] from a string. |
49 | | /// |
50 | | /// String must conform to the constraints given in the type-level |
51 | | /// documentation. |
52 | 0 | pub const fn new(s: &'a str) -> Result<Self> { |
53 | 0 | let input = s.as_bytes(); |
54 | | |
55 | 0 | match input.len() { |
56 | 0 | 1..=Self::MAX_LENGTH => { |
57 | 0 | let mut i = 0; |
58 | | |
59 | 0 | while i < input.len() { |
60 | 0 | if !matches!(input[i], b'a'..=b'z' | b'0'..=b'9' | b'-') { |
61 | 0 | return Err(Error::ParamNameInvalid); |
62 | 0 | } |
63 | | |
64 | 0 | i += 1; |
65 | | } |
66 | | |
67 | 0 | Ok(Self(s)) |
68 | | } |
69 | 0 | _ => Err(Error::ParamNameInvalid), |
70 | | } |
71 | 0 | } |
72 | | |
73 | | /// Parse an [`Ident`] from a string, panicking on parse errors. |
74 | | /// |
75 | | /// This function exists as a workaround for `unwrap` not yet being |
76 | | /// stable in `const fn` contexts, and is intended to allow the result to |
77 | | /// be bound to a constant value. |
78 | 0 | pub const fn new_unwrap(s: &'a str) -> Self { |
79 | 0 | assert!(!s.is_empty(), "PHC ident string can't be empty"); |
80 | 0 | assert!(s.len() <= Self::MAX_LENGTH, "PHC ident string too long"); |
81 | | |
82 | 0 | match Self::new(s) { |
83 | 0 | Ok(ident) => ident, |
84 | 0 | Err(_) => panic!("invalid PHC string format identifier"), |
85 | | } |
86 | 0 | } |
87 | | |
88 | | /// Borrow this ident as a `str` |
89 | 0 | pub fn as_str(&self) -> &'a str { |
90 | 0 | self.0 |
91 | 0 | } |
92 | | } |
93 | | |
94 | | impl<'a> AsRef<str> for Ident<'a> { |
95 | 0 | fn as_ref(&self) -> &str { |
96 | 0 | self.as_str() |
97 | 0 | } |
98 | | } |
99 | | |
100 | | impl<'a> Deref for Ident<'a> { |
101 | | type Target = str; |
102 | | |
103 | 0 | fn deref(&self) -> &str { |
104 | 0 | self.as_str() |
105 | 0 | } |
106 | | } |
107 | | |
108 | | // Note: this uses `TryFrom` instead of `FromStr` to support a lifetime on |
109 | | // the `str` the value is being parsed from. |
110 | | impl<'a> TryFrom<&'a str> for Ident<'a> { |
111 | | type Error = Error; |
112 | | |
113 | 0 | fn try_from(s: &'a str) -> Result<Self> { |
114 | 0 | Self::new(s) |
115 | 0 | } |
116 | | } |
117 | | |
118 | | impl<'a> fmt::Display for Ident<'a> { |
119 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
120 | 0 | f.write_str(&*self) |
121 | 0 | } |
122 | | } |
123 | | |
124 | | impl<'a> fmt::Debug for Ident<'a> { |
125 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
126 | 0 | f.debug_tuple("Ident").field(&self.as_ref()).finish() |
127 | 0 | } |
128 | | } |
129 | | |
130 | | #[cfg(test)] |
131 | | mod tests { |
132 | | use super::{Error, Ident}; |
133 | | |
134 | | // Invalid ident examples |
135 | | const INVALID_EMPTY: &str = ""; |
136 | | const INVALID_CHAR: &str = "argon2;d"; |
137 | | const INVALID_TOO_LONG: &str = "012345678911234567892123456789312"; |
138 | | const INVALID_CHAR_AND_TOO_LONG: &str = "0!2345678911234567892123456789312"; |
139 | | |
140 | | #[test] |
141 | | fn parse_valid() { |
142 | | let valid_examples = ["6", "x", "argon2d", "01234567891123456789212345678931"]; |
143 | | |
144 | | for &example in &valid_examples { |
145 | | assert_eq!(example, &*Ident::new(example).unwrap()); |
146 | | } |
147 | | } |
148 | | |
149 | | #[test] |
150 | | fn reject_empty() { |
151 | | assert_eq!(Ident::new(INVALID_EMPTY), Err(Error::ParamNameInvalid)); |
152 | | } |
153 | | |
154 | | #[test] |
155 | | fn reject_invalid() { |
156 | | assert_eq!(Ident::new(INVALID_CHAR), Err(Error::ParamNameInvalid)); |
157 | | } |
158 | | |
159 | | #[test] |
160 | | fn reject_too_long() { |
161 | | assert_eq!(Ident::new(INVALID_TOO_LONG), Err(Error::ParamNameInvalid)); |
162 | | } |
163 | | |
164 | | #[test] |
165 | | fn reject_invalid_char_and_too_long() { |
166 | | assert_eq!( |
167 | | Ident::new(INVALID_CHAR_AND_TOO_LONG), |
168 | | Err(Error::ParamNameInvalid) |
169 | | ); |
170 | | } |
171 | | } |