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) {
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::Param;
115
116 #[test]
117 fn new() {
118 let param: Param = unsafe { Param::new("name", Some("value")) };
119 assert_eq!(param.name, "name");
120 assert_eq!(param.value, Some("value"));
121 }
122
123 #[test]
124 fn from_str() {
125 let param: Param = unsafe { Param::from_str("name") };
126 assert_eq!(param.name, "name");
127 assert_eq!(param.value, None);
128
129 let param: Param = unsafe { Param::from_str("name=") };
130 assert_eq!(param.name, "name");
131 assert_eq!(param.value, Some(""));
132
133 let param: Param = unsafe { Param::from_str("name=value") };
134 assert_eq!(param.name, "name");
135 assert_eq!(param.value, Some("value"));
136 }
137
138 #[test]
139 fn properties() {
140 let param: Param = unsafe { Param::new("name", Some("value")) };
141 assert_eq!(param.name(), "name");
142 assert_eq!(param.value(), Some("value"));
143
144 let param: Param = unsafe { Param::new("name", None) };
145 assert_eq!(param.name(), "name");
146 assert_eq!(param.value(), None);
147 }
148
149 #[test]
150 fn display() {
151 let param: Param = unsafe { Param::new("name", Some("value")) };
152 assert_eq!(param.to_string(), "name=value");
153
154 let param: Param = unsafe { Param::new("name", None) };
155 assert_eq!(param.to_string(), "name");
156 }
157}