1use std::fmt::Display;
2
3use nom::{
4 IResult, Parser,
5 bytes::complete::{take_till, take_until},
6 character::complete::char,
7 error::context,
8 multi::many_m_n,
9 sequence::separated_pair,
10};
11use serde::{Deserialize, Serialize};
12
13use crate::{IErr, error::NixUriError};
14
15#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
16#[cfg_attr(test, serde(deny_unknown_fields))]
17pub struct LocationParameters {
18 dir: Option<String>,
22 #[serde(rename = "narHash")]
26 nar_hash: Option<String>,
27 rev: Option<String>,
29 r#ref: Option<String>,
31 branch: Option<String>,
32 submodules: Option<String>,
33 shallow: Option<String>,
34 host: Option<String>,
36 #[serde(rename = "revCount")]
38 rev_count: Option<String>,
39 #[serde(rename = "lastModified")]
41 last_modified: Option<String>,
42 arbitrary: Vec<(String, String)>,
45}
46
47impl Display for LocationParameters {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 let mut first = true;
52 let mut write_param = |key: &str, value: &str| {
53 if !first {
54 write!(f, "&")?;
55 }
56 first = false;
57 write!(f, "{key}={value}")
58 };
59
60 if let Some(dir) = &self.dir {
61 write_param("dir", dir)?;
62 }
63 if let Some(branch) = &self.branch {
64 write_param("branch", branch)?;
65 }
66 if let Some(host) = &self.host {
67 write_param("host", host)?;
68 }
69 if let Some(r#ref) = &self.r#ref {
70 write_param("ref", r#ref)?;
71 }
72 if let Some(rev) = &self.rev {
73 write_param("rev", rev)?;
74 }
75 if let Some(nar_hash) = &self.nar_hash {
76 write_param("nar_hash", nar_hash)?;
77 }
78 if let Some(submodules) = &self.submodules {
79 write_param("submodules", submodules)?;
80 }
81 if let Some(shallow) = &self.shallow {
82 write_param("shallow", shallow)?;
83 }
84 for (key, value) in &self.arbitrary {
85 write_param(key, value)?;
86 }
87
88 Ok(())
89 }
90}
91
92impl LocationParameters {
93 pub fn parse(input: &str) -> IResult<&str, Self, IErr<&str>> {
94 let (rest, param_values) = context(
95 "location parameters",
96 many_m_n(
97 0,
98 11,
99 separated_pair(
100 take_until("="),
101 char('='),
102 take_till(|c| c == '&' || c == '#'),
103 ),
104 ),
105 )
106 .parse(input)?;
107
108 let mut params = Self::default();
109 for (param, value) in param_values {
110 if let Ok(param) = param.parse() {
114 match param {
115 LocationParamKeys::Dir => params.set_dir(Some(value.into())),
116 LocationParamKeys::NarHash => params.set_nar_hash(Some(value.into())),
117 LocationParamKeys::Host => params.set_host(Some(value.into())),
118 LocationParamKeys::Ref => params.set_ref(Some(value.into())),
119 LocationParamKeys::Rev => params.set_rev(Some(value.into())),
120 LocationParamKeys::Branch => params.set_branch(Some(value.into())),
121 LocationParamKeys::Submodules => params.set_submodules(Some(value.into())),
122 LocationParamKeys::Shallow => params.set_shallow(Some(value.into())),
123 LocationParamKeys::Arbitrary(param) => {
124 params.add_arbitrary((param, value.into()));
125 }
126 }
127 }
128 }
129 Ok((rest, params))
130 }
131
132 pub fn dir(&mut self, dir: Option<String>) -> &mut Self {
133 self.dir = dir;
134 self
135 }
136
137 pub fn nar_hash(&mut self, nar_hash: Option<String>) -> &mut Self {
138 self.nar_hash = nar_hash;
139 self
140 }
141
142 pub fn host(&mut self, host: Option<String>) -> &mut Self {
143 self.host = host;
144 self
145 }
146 pub fn rev(&mut self, rev: Option<String>) -> &mut Self {
147 self.rev = rev;
148 self
149 }
150 pub fn r#ref(&mut self, r#ref: Option<String>) -> &mut Self {
151 self.r#ref = r#ref;
152 self
153 }
154
155 pub fn set_dir(&mut self, dir: Option<String>) {
156 self.dir = dir;
157 }
158
159 pub fn set_nar_hash(&mut self, nar_hash: Option<String>) {
160 self.nar_hash = nar_hash;
161 }
162
163 pub fn set_rev(&mut self, rev: Option<String>) {
164 self.rev = rev;
165 }
166
167 pub fn set_ref(&mut self, r#ref: Option<String>) {
168 self.r#ref = r#ref;
169 }
170
171 pub fn set_host(&mut self, host: Option<String>) {
172 self.host = host;
173 }
174
175 pub fn rev_count_mut(&mut self) -> &mut Option<String> {
176 &mut self.rev_count
177 }
178
179 pub fn set_branch(&mut self, branch: Option<String>) {
180 self.branch = branch;
181 }
182
183 pub fn set_submodules(&mut self, submodules: Option<String>) {
184 self.submodules = submodules;
185 }
186
187 pub fn set_shallow(&mut self, shallow: Option<String>) {
188 self.shallow = shallow;
189 }
190 pub fn add_arbitrary(&mut self, arbitrary: (String, String)) {
191 self.arbitrary.push(arbitrary);
192 }
193 pub const fn get_rev(&self) -> Option<&String> {
194 self.rev.as_ref()
195 }
196 pub const fn get_ref(&self) -> Option<&String> {
197 self.r#ref.as_ref()
198 }
199}
200
201pub enum LocationParamKeys {
202 Dir,
203 NarHash,
204 Host,
205 Ref,
206 Rev,
207 Branch,
208 Submodules,
209 Shallow,
210 Arbitrary(String),
211}
212
213impl std::str::FromStr for LocationParamKeys {
214 type Err = NixUriError;
215
216 fn from_str(s: &str) -> Result<Self, Self::Err> {
217 match s {
218 "dir" | "&dir" => Ok(Self::Dir),
219 "nar_hash" | "&nar_hash" => Ok(Self::NarHash),
220 "host" | "&host" => Ok(Self::Host),
221 "rev" | "&rev" => Ok(Self::Rev),
222 "ref" | "&ref" => Ok(Self::Ref),
223 "branch" | "&branch" => Ok(Self::Branch),
224 "submodules" | "&submodules" => Ok(Self::Submodules),
225 "shallow" | "&shallow" => Ok(Self::Shallow),
226 arbitrary => Ok(Self::Arbitrary(arbitrary.into())),
227 }
229 }
230}
231
232#[cfg(test)]
233mod inc_parse {
234 use super::*;
235 #[test]
236 fn no_str() {
237 let expected = LocationParameters::default();
238 let in_str = "";
239 let (outstr, parsed_param) = LocationParameters::parse(in_str).unwrap();
240 assert_eq!("", outstr);
241 assert_eq!(expected, parsed_param);
242 }
243 #[test]
244 fn empty() {
245 let expected = LocationParameters::default();
246 let in_str = "";
247 let (rest, output) = LocationParameters::parse(in_str).unwrap();
248 assert_eq!("", rest);
249 assert_eq!(output, expected);
250 }
251 #[test]
252 fn empty_hash_terminated() {
253 let expected = LocationParameters::default();
254 let in_str = "#";
255 let (rest, output) = LocationParameters::parse(in_str).unwrap();
256 assert_eq!("#", rest);
257 assert_eq!(output, expected);
258 }
259 #[test]
260 fn dir() {
261 let mut expected = LocationParameters::default();
262 expected.dir(Some("foo".to_string()));
263
264 let in_str = "dir=foo";
265 let (rest, output) = LocationParameters::parse(in_str).unwrap();
266 assert_eq!("", rest);
267 assert_eq!(output, expected);
268
269 let in_str = "&dir=foo";
270 let (rest, output) = LocationParameters::parse(in_str).unwrap();
271 assert_eq!("", rest);
272 assert_eq!(output, expected);
273 let in_str = "dir=&dir=foo";
274 let (rest, output) = LocationParameters::parse(in_str).unwrap();
275 assert_eq!("", rest);
276 assert_eq!(output, expected);
277
278 expected.dir(Some(String::new()));
279 let in_str = "dir=";
280 let (rest, output) = LocationParameters::parse(in_str).unwrap();
281 assert_eq!("", rest);
282 assert_eq!(output, expected);
283 }
284 #[test]
285 fn dir_hash_term() {
286 let mut expected = LocationParameters::default();
287 expected.dir(Some("foo".to_string()));
288
289 let in_str = "dir=foo#fizz";
290 let (rest, output) = LocationParameters::parse(in_str).unwrap();
291 assert_eq!("#fizz", rest);
292 assert_eq!(output, expected);
293
294 let in_str = "&dir=foo#fizz";
295 let (rest, output) = LocationParameters::parse(in_str).unwrap();
296 assert_eq!("#fizz", rest);
297 assert_eq!(output, expected);
298 let in_str = "dir=&dir=foo#fizz";
299 let (rest, output) = LocationParameters::parse(in_str).unwrap();
300 assert_eq!("#fizz", rest);
301 assert_eq!(output, expected);
302
303 expected.dir(Some(String::new()));
304 let in_str = "dir=#fizz";
305 let (rest, output) = LocationParameters::parse(in_str).unwrap();
306 assert_eq!("#fizz", rest);
307 assert_eq!(output, expected);
308 }
309}