1use crate::parse::Error;
2use crate::parse::Error::InvalidParam;
3use std::fmt::{Display, Formatter};
4
5#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
15pub struct Param<'a> {
16 name: &'a str,
17 value: Option<&'a str>,
18}
19
20impl<'a> Param<'a> {
21 pub unsafe fn new(name: &'a str, value: Option<&'a str>) -> Self {
28 debug_assert!(Self::is_valid_name(name));
29 debug_assert!(value.iter().all(|v| Self::is_valid_value(v)));
30
31 Self { name, value }
32 }
33
34 pub unsafe fn from_str(param: &'a str) -> Self {
41 if let Some(eq) = param.as_bytes().iter().position(|c| *c == b'=') {
42 let (name, eq_value) = param.split_at(eq);
43 Self::new(name, Some(&eq_value[1..]))
44 } else {
45 Self::new(param, None)
46 }
47 }
48}
49
50impl<'a> TryFrom<&'a str> for Param<'a> {
51 type Error = Error;
52
53 fn try_from(param: &'a str) -> Result<Self, Self::Error> {
54 if let Some(eq) = param.as_bytes().iter().position(|c| *c == b'=') {
55 let (name, eq_value) = param.split_at(eq);
56 if Self::is_valid_name(name) && Self::is_valid_value(&eq_value[1..]) {
57 Ok(unsafe { Self::new(name, Some(&eq_value[1..])) })
58 } else {
59 Err(InvalidParam)
60 }
61 } else if Self::is_valid_name(param) {
62 Ok(unsafe { Self::new(param, None) })
63 } else {
64 Err(InvalidParam)
65 }
66 }
67}
68
69impl<'a> Param<'a> {
70 pub fn is_valid_name(name: &str) -> bool {
74 name.as_bytes().iter().all(|c| {
75 c.is_ascii_alphanumeric()
76 || (c.is_ascii_punctuation() && *c != b'&' && *c != b'#' && *c != b'=')
77 })
78 }
79
80 pub fn is_valid_value(value: &str) -> bool {
82 value.as_bytes().iter().all(|c| {
83 c.is_ascii_alphanumeric() || (c.is_ascii_punctuation() && *c != b'&' && *c != b'#')
84 })
85 }
86}
87
88impl<'a> Param<'a> {
89 pub const fn name(&self) -> &str {
93 self.name
94 }
95
96 pub const fn value(&self) -> Option<&str> {
98 self.value
99 }
100}
101
102impl<'a> Display for Param<'a> {
103 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
104 write!(f, "{}", self.name)?;
105 if let Some(value) = self.value {
106 write!(f, "={}", value)?;
107 }
108 Ok(())
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use crate::parse::Error::InvalidParam;
115 use crate::Param;
116
117 #[test]
118 fn new() {
119 let param: Param = unsafe { Param::new("name", Some("value")) };
120 assert_eq!(param.name, "name");
121 assert_eq!(param.value, Some("value"));
122 }
123
124 #[test]
125 fn from_str() {
126 let param: Param = unsafe { Param::from_str("name") };
127 assert_eq!(param.name, "name");
128 assert_eq!(param.value, None);
129
130 let param: Param = unsafe { Param::from_str("name=") };
131 assert_eq!(param.name, "name");
132 assert_eq!(param.value, Some(""));
133
134 let param: Param = unsafe { Param::from_str("name=value") };
135 assert_eq!(param.name, "name");
136 assert_eq!(param.value, Some("value"));
137 }
138
139 #[test]
140 fn try_from_str() {
141 let param = Param::try_from("name").unwrap();
142 assert_eq!(param.name(), "name");
143 assert_eq!(param.value(), None);
144
145 let param = Param::try_from("name=value").unwrap();
146 assert_eq!(param.name(), "name");
147 assert_eq!(param.value(), Some("value"));
148
149 let param = Param::try_from("name=").unwrap();
150 assert_eq!(param.name(), "name");
151 assert_eq!(param.value(), Some(""));
152
153 let param = Param::try_from("").unwrap();
154 assert_eq!(param.name(), "");
155 assert_eq!(param.value(), None);
156
157 assert_eq!(Param::try_from("name=val&ue"), Err(InvalidParam));
158 assert_eq!(Param::try_from("na&me"), Err(InvalidParam));
159 assert_eq!(Param::try_from("na#me"), Err(InvalidParam));
160 assert_eq!(Param::try_from("name=val#ue"), Err(InvalidParam));
161 }
162
163 #[test]
164 fn properties() {
165 let param: Param = unsafe { Param::new("name", Some("value")) };
166 assert_eq!(param.name(), "name");
167 assert_eq!(param.value(), Some("value"));
168
169 let param: Param = unsafe { Param::new("name", None) };
170 assert_eq!(param.name(), "name");
171 assert_eq!(param.value(), None);
172 }
173
174 #[test]
175 fn display() {
176 let param: Param = unsafe { Param::new("name", Some("value")) };
177 assert_eq!(param.to_string(), "name=value");
178
179 let param: Param = unsafe { Param::new("name", None) };
180 assert_eq!(param.to_string(), "name");
181 }
182}