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