httpsig/message_component/
component_param.rs1use crate::error::{HttpSigError, HttpSigResult};
2use sfv::{Parser, SerializeValue};
3
4type IndexSet<K> = indexmap::IndexSet<K, fxhash::FxBuildHasher>;
5
6#[derive(PartialEq, Eq, Hash, Debug, Clone)]
8pub enum HttpMessageComponentParam {
11 Sf,
13 Key(String),
16 Bs,
18 Tr,
20 Req,
22 Name(String),
25}
26
27impl From<HttpMessageComponentParam> for String {
28 fn from(val: HttpMessageComponentParam) -> Self {
29 match val {
30 HttpMessageComponentParam::Sf => "sf".to_string(),
31 HttpMessageComponentParam::Key(val) => format!("key=\"{val}\""),
32 HttpMessageComponentParam::Bs => "bs".to_string(),
33 HttpMessageComponentParam::Tr => "tr".to_string(),
34 HttpMessageComponentParam::Req => "req".to_string(),
35 HttpMessageComponentParam::Name(v) => format!("name=\"{v}\""),
36 }
37 }
38}
39
40impl TryFrom<(&str, &sfv::BareItem)> for HttpMessageComponentParam {
41 type Error = HttpSigError;
42 fn try_from((key, val): (&str, &sfv::BareItem)) -> Result<Self, Self::Error> {
43 match key {
44 "sf" => Ok(Self::Sf),
45 "bs" => Ok(Self::Bs),
46 "tr" => Ok(Self::Tr),
47 "req" => Ok(Self::Req),
48 "name" => {
49 let name = val.as_str().ok_or(HttpSigError::InvalidComponentParam(
50 "Invalid http field param: name".to_string(),
51 ))?;
52 Ok(Self::Name(name.to_string()))
53 }
54 "key" => {
55 let key = val.as_str().ok_or(HttpSigError::InvalidComponentParam(
56 "Invalid http field param: key".to_string(),
57 ))?;
58 Ok(Self::Key(key.to_string()))
59 }
60 _ => Err(HttpSigError::InvalidComponentParam(format!(
61 "Invalid http field param: {key}"
62 ))),
63 }
64 }
65}
66
67#[derive(PartialEq, Eq, Debug, Clone)]
68pub struct HttpMessageComponentParams(pub IndexSet<HttpMessageComponentParam>);
70
71impl std::hash::Hash for HttpMessageComponentParams {
72 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
73 let mut params = self.0.iter().map(|v| v.clone().into()).collect::<Vec<String>>();
74 params.sort();
75 params.hash(state);
76 }
77}
78
79impl TryFrom<&sfv::Parameters> for HttpMessageComponentParams {
80 type Error = HttpSigError;
81 fn try_from(val: &sfv::Parameters) -> Result<Self, Self::Error> {
82 let hs = val
83 .iter()
84 .map(|(k, v)| HttpMessageComponentParam::try_from((k.as_str(), v)))
85 .collect::<Result<IndexSet<_>, _>>()?;
86 Ok(Self(hs))
87 }
88}
89impl std::fmt::Display for HttpMessageComponentParams {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 if !self.0.is_empty() {
92 write!(
93 f,
94 ";{}",
95 self.0.iter().map(|v| v.clone().into()).collect::<Vec<String>>().join(";")
96 )
97 } else {
98 Ok(())
99 }
100 }
101}
102
103pub(super) fn handle_params_sf(field_values: &mut [String]) -> HttpSigResult<()> {
106 let parsed_list = field_values
107 .iter()
108 .map(|v| {
109 if let Ok(list) = Parser::parse_list(v.as_bytes()) {
110 list.serialize_value()
111 } else if let Ok(dict) = Parser::parse_dictionary(v.as_bytes()) {
112 dict.serialize_value()
113 } else {
114 Err("invalid structured field value for sf")
115 }
116 })
117 .collect::<Result<Vec<_>, _>>()
118 .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to parse structured field value: {e}")))?;
119
120 field_values.iter_mut().zip(parsed_list).for_each(|(v, p)| {
121 *v = p;
122 });
123
124 Ok(())
125}
126
127pub(super) fn handle_params_key_into(field_values: &[String], key: &str) -> HttpSigResult<Vec<String>> {
130 let dicts = field_values
131 .iter()
132 .map(|v| Parser::parse_dictionary(v.as_bytes()))
133 .collect::<Result<Vec<_>, _>>()
134 .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to parse structured field value: {e}")))?;
135
136 let found_entries = dicts
137 .into_iter()
138 .filter_map(|dict| {
139 dict.get(key).map(|v| {
140 let sfvalue: sfv::List = vec![v.clone()];
141 sfvalue.serialize_value()
142 })
143 })
144 .collect::<Result<Vec<_>, _>>()
145 .map_err(|e| HttpSigError::InvalidComponentParam(format!("Failed to serialize structured field value: {e}")))?;
146
147 Ok(found_entries)
148}
149
150mod tests {
153 #[allow(unused)]
154 use super::*;
155
156 #[test]
157 fn parser_test() {
158 let item_header_input = "12.445;foo=bar";
160 let item = Parser::parse_item(item_header_input.as_bytes()).unwrap();
161 assert_eq!(item.serialize_value().unwrap(), item_header_input);
162
163 let list_header_input = " 1; a=tok, (\"foo\" \"bar\" );baz, ( )";
165 let list = Parser::parse_list(list_header_input.as_bytes()).unwrap();
166 assert_eq!(list.serialize_value().unwrap(), "1;a=tok, (\"foo\" \"bar\");baz, ()");
167
168 let dict_header_input = "a=?0, b, c; foo=bar, rating=1.5, fruits=(apple pear), d";
170 let dict = Parser::parse_dictionary(dict_header_input.as_bytes()).unwrap();
171 assert_eq!(
172 dict.serialize_value().unwrap(),
173 "a=?0, b, c;foo=bar, rating=1.5, fruits=(apple pear), d"
174 );
175 }
176}