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