1use std::{borrow::Cow, collections::HashMap};
9
10use nom::{
11 branch::alt,
12 bytes::complete::tag_no_case,
13 character::complete::{char, space0, space1},
14 combinator::map,
15 multi::many0,
16 sequence::{preceded, separated_pair, tuple},
17 IResult,
18};
19
20use crate::{
21 parser::core::{nil, nstring_utf8, string_utf8},
22 Response,
23};
24
25fn id_param(i: &[u8]) -> IResult<&[u8], (&str, Option<&str>)> {
29 separated_pair(string_utf8, space1, nstring_utf8)(i)
30}
31
32fn id_param_list_not_nil(i: &[u8]) -> IResult<&[u8], HashMap<&str, &str>> {
36 map(
37 tuple((
38 char('('),
39 id_param,
40 many0(tuple((space1, id_param))),
41 preceded(space0, char(')')),
42 )),
43 |(_, first_param, rest_params, _)| {
44 let mut params = vec![first_param];
45 for (_, p) in rest_params {
46 params.push(p)
47 }
48
49 params
50 .into_iter()
51 .filter(|(_k, v)| v.is_some())
52 .map(|(k, v)| (k, v.unwrap()))
53 .collect()
54 },
55 )(i)
56}
57
58fn id_param_list(i: &[u8]) -> IResult<&[u8], Option<HashMap<&str, &str>>> {
62 alt((map(id_param_list_not_nil, Some), map(nil, |_| None)))(i)
63}
64
65pub(crate) fn resp_id(i: &[u8]) -> IResult<&[u8], Response<'_>> {
68 let (rest, map) = map(
69 tuple((tag_no_case("ID"), space1, id_param_list)),
70 |(_id, _sp, p)| p,
71 )(i)?;
72
73 Ok((
74 rest,
75 Response::Id(map.map(|m| {
76 m.into_iter()
77 .map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v)))
78 .collect()
79 })),
80 ))
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use assert_matches::assert_matches;
87
88 #[test]
89 fn test_id_param() {
90 assert_matches!(
91 id_param(br#""name" "Cyrus""#),
92 Ok((_, (name, value))) => {
93 assert_eq!(name, "name");
94 assert_eq!(value, Some("Cyrus"));
95 }
96 );
97
98 assert_matches!(
99 id_param(br#""name" NIL"#),
100 Ok((_, (name, value))) => {
101 assert_eq!(name, "name");
102 assert_eq!(value, None);
103 }
104 );
105 }
106
107 #[test]
108 fn test_id_param_list_not_nil() {
109 assert_matches!(
110 id_param_list_not_nil(br#"("name" "Cyrus" "version" "1.5" "os" "sunos" "os-version" "5.5" "support-url" "mailto:cyrus-bugs+@andrew.cmu.edu")"#),
111 Ok((_, params)) => {
112 assert_eq!(
113 params,
114 vec![
115 ("name", "Cyrus"),
116 ("version", "1.5"),
117 ("os", "sunos"),
118 ("os-version", "5.5"),
119 ("support-url", "mailto:cyrus-bugs+@andrew.cmu.edu"),
120 ].into_iter()
121 .collect()
122 );
123 }
124 );
125 }
126
127 #[test]
128 fn test_id_param_list() {
129 assert_matches!(
130 id_param_list(br#"("name" "Cyrus" "version" "1.5" "os" "sunos" "os-version" "5.5" "support-url" "mailto:cyrus-bugs+@andrew.cmu.edu")"#),
131 Ok((_, Some(params))) => {
132 assert_eq!(
133 params,
134 vec![
135 ("name", "Cyrus"),
136 ("version", "1.5"),
137 ("os", "sunos"),
138 ("os-version", "5.5"),
139 ("support-url", "mailto:cyrus-bugs+@andrew.cmu.edu"),
140 ].into_iter()
141 .collect()
142 );
143 }
144 );
145
146 assert_matches!(
147 id_param_list(br##"NIL"##),
148 Ok((_, params)) => {
149 assert_eq!(params, None);
150 }
151 );
152 }
153
154 #[test]
155 fn test_resp_id() {
156 assert_matches!(
157 resp_id(br#"ID ("name" "Cyrus" "version" "1.5" "os" "sunos" "os-version" "5.5" "support-url" "mailto:cyrus-bugs+@andrew.cmu.edu")"#),
158 Ok((_, Response::Id(Some(id_info)))) => {
159 assert_eq!(
160 id_info,
161 vec![
162 ("name", "Cyrus"),
163 ("version", "1.5"),
164 ("os", "sunos"),
165 ("os-version", "5.5"),
166 ("support-url", "mailto:cyrus-bugs+@andrew.cmu.edu"),
167 ].into_iter()
168 .map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v)))
169 .collect()
170 );
171 }
172 );
173
174 assert_matches!(
177 resp_id(br#"ID ("name" "Cyrus" "version" "1.5" "os" NIL "os-version" NIL "support-url" "mailto:cyrus-bugs+@andrew.cmu.edu")"#),
178 Ok((_, Response::Id(Some(id_info)))) => {
179 assert_eq!(
180 id_info,
181 vec![
182 ("name", "Cyrus"),
183 ("version", "1.5"),
184 ("support-url", "mailto:cyrus-bugs+@andrew.cmu.edu"),
185 ].into_iter()
186 .map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v)))
187 .collect()
188 );
189 }
190 );
191
192 assert_matches!(
193 resp_id(br##"ID NIL"##),
194 Ok((_, Response::Id(id_info))) => {
195 assert_eq!(id_info, None);
196 }
197 );
198
199 assert_matches!(
200 resp_id(br#"ID ("name" "Archiveopteryx" "version" "3.2.0" "compile-time" "Feb 6 2023 19:59:14" "homepage-url" "http://archiveopteryx.org" "release-url" "http://archiveopteryx.org/3.2.0" )"#),
201 Ok((_, Response::Id(Some(id_info)))) => {
202 assert_eq!(
203 id_info,
204 vec![
205 ("name", "Archiveopteryx"),
206 ("version", "3.2.0"),
207 ("compile-time", "Feb 6 2023 19:59:14"),
208 ("homepage-url", "http://archiveopteryx.org"),
209 ("release-url", "http://archiveopteryx.org/3.2.0"),
210 ].into_iter()
211 .map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v)))
212 .collect()
213 );
214 }
215 );
216 }
217}