1use std::{
41 collections::BTreeMap,
42 error,
43 fmt::{self, Display},
44 marker::PhantomData,
45 num,
46 str,
47 str::{FromStr, ParseBoolError},
48};
49
50use serde::{
51 Deserialize,
52 de::{self, DeserializeOwned, IntoDeserializer, Visitor, value::SeqDeserializer},
53 forward_to_deserialize_any,
54};
55use winnow::Parser;
56
57use super::parser::{Item, ini_file};
58
59#[derive(Debug, Clone)]
60pub enum Error {
61 Parse(String),
65
66 Custom(String),
70
71 UnexpectedEof,
75
76 InvalidState,
80}
81
82impl From<num::ParseIntError> for Error {
83 fn from(e: num::ParseIntError) -> Self {
84 Error::Custom(e.to_string())
85 }
86}
87
88impl From<num::ParseFloatError> for Error {
89 fn from(e: num::ParseFloatError) -> Self {
90 Error::Custom(e.to_string())
91 }
92}
93
94impl From<ParseBoolError> for Error {
95 fn from(e: ParseBoolError) -> Self {
96 Error::Custom(e.to_string())
97 }
98}
99
100impl Display for Error {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 match self {
103 Error::Custom(msg) => write!(f, "{msg}"),
104 Error::Parse(msg) => write!(f, "{msg}"),
105 Error::UnexpectedEof => write!(f, "internal consistency error: unexpected EOF"),
106 Error::InvalidState => write!(f, "internal consistency error"),
107 }
108 }
109}
110
111impl error::Error for Error {
112 fn description(&self) -> &str {
113 "deserialization error"
114 }
115}
116
117impl de::Error for Error {
118 fn custom<T: Display>(msg: T) -> Self {
119 Error::Custom(msg.to_string())
120 }
121}
122
123pub type Result<T> = std::result::Result<T, Error>;
124
125impl IntoDeserializer<'_, Error> for Item {
126 type Deserializer = ItemDeserializer<Error>;
127
128 fn into_deserializer(self) -> Self::Deserializer {
129 ItemDeserializer::new(self)
130 }
131}
132
133struct Deserializer {
135 input: BTreeMap<String, Item>,
136}
137
138impl<'a> TryFrom<&'a str> for Deserializer {
145 type Error = Error;
146
147 fn try_from(contents: &'a str) -> Result<Self> {
148 let input = ini_file
149 .parse(contents)
150 .map_err(|err| Error::Custom(format!("{err}")))?;
151
152 Ok(Deserializer { input })
153 }
154}
155
156impl<'de> de::Deserializer<'de> for &mut Deserializer {
157 type Error = Error;
158
159 fn is_human_readable(&self) -> bool {
160 true
161 }
162
163 fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
164 where
165 V: serde::de::Visitor<'de>,
166 {
167 visitor.visit_map(self.input.clone().into_deserializer())
168 }
169
170 forward_to_deserialize_any! {
171 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
172 byte_buf unit unit_struct newtype_struct tuple_struct
173 struct identifier ignored_any enum option map tuple seq
174 }
175}
176
177pub struct ItemDeserializer<E> {
178 item: Item,
179 marker: PhantomData<E>,
180}
181
182impl<E> ItemDeserializer<E> {
183 pub fn new(item: Item) -> Self {
184 ItemDeserializer {
185 item,
186 marker: PhantomData,
187 }
188 }
189}
190
191impl<'de> de::Deserializer<'de> for ItemDeserializer<Error> {
192 type Error = Error;
193
194 fn is_human_readable(&self) -> bool {
195 true
196 }
197
198 fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
199 where
200 V: serde::de::Visitor<'de>,
201 {
202 match &self.item {
203 Item::Value(value) => visitor.visit_str(value),
204 Item::List(vec) => visitor.visit_seq(vec.clone().into_deserializer()),
205 }
206 }
207
208 fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
209 where
210 V: serde::de::Visitor<'de>,
211 {
212 let de = match self.item {
214 Item::Value(value) => {
216 SeqDeserializer::new(vec![SeqItemDeserializer(value.clone())].into_iter())
217 }
218 Item::List(values) => {
220 let mut items = Vec::new();
221 for value in values.clone() {
222 items.push(SeqItemDeserializer(value.clone()));
223 }
224 SeqDeserializer::new(items.into_iter())
225 }
226 };
227 visitor
228 .visit_seq(de)
229 .map_err(|e| Error::Custom(e.to_string()))
230 }
231
232 fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
233 visitor.visit_bool(FromStr::from_str(self.item.value_or_error()?)?)
234 }
235
236 fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
237 visitor.visit_i8(FromStr::from_str(self.item.value_or_error()?)?)
238 }
239
240 fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
241 visitor.visit_i16(FromStr::from_str(self.item.value_or_error()?)?)
242 }
243
244 fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
245 visitor.visit_i32(FromStr::from_str(self.item.value_or_error()?)?)
246 }
247
248 fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
249 visitor.visit_i64(FromStr::from_str(self.item.value_or_error()?)?)
250 }
251
252 fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
253 visitor.visit_i128(FromStr::from_str(self.item.value_or_error()?)?)
254 }
255
256 fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
257 visitor.visit_u8(FromStr::from_str(self.item.value_or_error()?)?)
258 }
259
260 fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
261 visitor.visit_u16(FromStr::from_str(self.item.value_or_error()?)?)
262 }
263
264 fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
265 visitor.visit_u32(FromStr::from_str(self.item.value_or_error()?)?)
266 }
267
268 fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
269 visitor.visit_u64(FromStr::from_str(self.item.value_or_error()?)?)
270 }
271
272 fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
273 visitor.visit_u128(FromStr::from_str(self.item.value_or_error()?)?)
274 }
275
276 fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
277 visitor.visit_f32(FromStr::from_str(self.item.value_or_error()?)?)
278 }
279
280 fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
281 visitor.visit_f64(FromStr::from_str(self.item.value_or_error()?)?)
282 }
283
284 forward_to_deserialize_any! {
285 char str string bytes
286 byte_buf unit unit_struct newtype_struct tuple tuple_struct
287 struct identifier ignored_any enum option map
288 }
289}
290
291struct SeqItemDeserializer(String);
293
294impl<'de> de::Deserializer<'de> for SeqItemDeserializer {
295 type Error = serde::de::value::Error;
296
297 fn deserialize_any<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
298 where
299 V: Visitor<'de>,
300 {
301 visitor.visit_str(&self.0)
302 }
303
304 fn deserialize_u64<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
305 where
306 V: Visitor<'de>,
307 {
308 visitor.visit_u64(self.0.parse().unwrap())
309 }
310
311 forward_to_deserialize_any! {
312 bool i8 i16 i32 i64 u8 u16 u32 f32 f64 char str string bytes
313 byte_buf unit unit_struct newtype_struct tuple tuple_struct
314 map struct identifier ignored_any enum option seq
315 }
316}
317
318impl IntoDeserializer<'_> for SeqItemDeserializer {
319 type Deserializer = SeqItemDeserializer;
320 fn into_deserializer(self) -> Self::Deserializer {
321 SeqItemDeserializer(self.0)
322 }
323}
324
325pub fn from_str<T: DeserializeOwned>(s: &str) -> Result<T> {
326 let mut de = Deserializer::try_from(s)?;
327 let value = Deserialize::deserialize(&mut de)?;
328 Ok(value)
329}
330
331#[cfg(test)]
332mod tests {
333 use serde::Deserialize;
334
335 use super::*;
336
337 #[derive(Deserialize, Clone, PartialEq, Default, Debug)]
338 struct TestModel {
339 builddate: i64,
340 builddir: String,
341 buildenv: Vec<String>,
342 format: String,
343 installed: Vec<String>,
344 options: Vec<String>,
345 packager: String,
346 pkgarch: String,
347 pkgbase: String,
348 pkgbuild_sha256sum: String,
349 pkgname: String,
350 pkgver: String,
351 }
352
353 const TEST_INPUT: &str = "
354 builddate = 1
355 builddir = /build
356 buildenv = envfoo
357 buildenv = envbar
358 format = 1
359 installed = bar-1.2.3-1-any
360 installed = beh-2.2.3-4-any
361 options = some_option
362 options = !other_option
363 options = !other_optionaaaaa
364 packager = Foobar McFooface <foobar@mcfooface.org>
365 pkgarch = any
366 pkgbase = foo
367 pkgbuild_sha256sum = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
368 pkgname = foo
369 pkgver = 1:1.0.0-1";
370
371 fn expected() -> TestModel {
372 TestModel {
373 builddate: 1,
374 builddir: "/build".into(),
375 buildenv: vec!["envfoo".into(), "envbar".into()],
376 format: "1".into(),
377 installed: vec!["bar-1.2.3-1-any".into(), "beh-2.2.3-4-any".into()],
378 options: vec![
379 "some_option".into(),
380 "!other_option".into(),
381 "!other_optionaaaaa".into(),
382 ],
383 packager: "Foobar McFooface <foobar@mcfooface.org>".into(),
384 pkgarch: "any".into(),
385 pkgbase: "foo".into(),
386 pkgbuild_sha256sum: "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"
387 .into(),
388 pkgname: "foo".into(),
389 pkgver: "1:1.0.0-1".into(),
390 }
391 }
392
393 #[test]
394 fn deserialize() {
395 let v = from_str::<TestModel>(TEST_INPUT).unwrap();
396 assert_eq!(expected(), v);
397 }
398
399 #[derive(Deserialize, Clone, PartialEq, Default, Debug)]
400 struct TypeTestModel {
401 i64: i64,
402 i32: i32,
403 u64: u64,
404 u32: u32,
405 list: Vec<String>,
406 u64_list: Vec<u64>,
407 bool: bool,
408 }
409
410 const TYPE_TEST_INPUT: &str = "
411 i64 = -64
412 i32 = -32
413 u64 = 64
414 u32 = 32
415 list = a
416 list = b
417 list = c
418 u64_list = 1
419 u64_list = 2
420 u64_list = 3
421 bool = true";
422 #[test]
423 fn deserialize_types() {
424 let value = from_str::<TypeTestModel>(TYPE_TEST_INPUT).unwrap();
425 assert_eq!(
426 TypeTestModel {
427 i64: -64,
428 i32: -32,
429 u64: 64,
430 u32: 32,
431 list: vec!["a".to_string(), "b".to_string(), "c".to_string()],
432 u64_list: vec![1, 2, 3],
433 bool: true
434 },
435 value
436 );
437 }
438
439 #[derive(Deserialize, Clone, PartialEq, Default, Debug)]
440 struct FlattenTestModelInner {
441 u64_list: Vec<u64>,
442 u64: u64,
443 }
444
445 #[derive(Deserialize, Clone, PartialEq, Default, Debug)]
446 struct FlattenTestModel {
447 #[serde(flatten)]
448 flattened: FlattenTestModelInner,
449 }
450
451 const FLATTEN_TEST_INPUT: &str = "
452 u64 = 42
453 u64_list = 1";
454
455 #[test]
466 fn deserialize_with_flatten() {
467 let expected = FlattenTestModelInner {
468 u64: 42,
469 u64_list: vec![1],
470 };
471
472 let value = from_str::<FlattenTestModelInner>(FLATTEN_TEST_INPUT).unwrap();
473 assert_eq!(expected, value);
474
475 let value = from_str::<FlattenTestModel>(FLATTEN_TEST_INPUT);
476 assert!(value.is_err());
477 }
478}