Skip to main content

otter_support/
toml-de.rs

1// Copyright 2020-2021 Ian Jackson and contributors to Otter
2// SPDX-License-Identifier: AGPL-3.0-or-later
3// There is NO WARRANTY.
4
5use crate::crates::*;
6use otter_base::crates::*;
7
8use std::fmt::{Debug, Display};
9use std::iter::Peekable;
10use std::slice;
11
12use fehler::throws;
13use if_chain::if_chain;
14use thiserror::Error;
15
16use serde::forward_to_deserialize_any;
17use serde::de::{
18  Deserialize, DeserializeOwned, Deserializer,
19  DeserializeSeed, EnumAccess,
20  IntoDeserializer, MapAccess,
21  SeqAccess, VariantAccess, Visitor
22};
23
24#[derive(Error,Debug)]
25pub enum Error {
26  #[error("deserialize failed (improper TOML structure?): {0}")]
27  Custom(Box<str>),
28  #[error("file has invalid TOML syntax: {0}")]
29  TomlSyntax(toml::de::Error),
30}
31
32impl serde::de::Error for Error {
33  fn custom<X: Display>(x: X) -> Self {
34    Error::Custom(x.to_string().into_boxed_str())
35  }
36}
37
38fn str_deserialize<'de, S: DeserializeSeed<'de>>
39  (seed: S, k: &'de str) -> Result<S::Value, Error>
40{
41  seed.deserialize(
42    k.into_deserializer()
43  )
44}
45
46pub struct TomlDe<'de>(pub &'de toml::Value);
47
48struct SA<'de>(slice::Iter<'de, toml::Value>);
49
50impl<'de> SeqAccess<'de> for SA<'de> {
51  type Error = Error;
52  #[throws(Error)]
53  fn next_element_seed<T: DeserializeSeed<'de>>
54    (&mut self, seed: T) -> Option<T::Value>
55  {
56    if let Some(elem) = (self.0).next() {
57      Some(seed.deserialize(TomlDe(elem))?)
58    } else {
59      None
60    }
61  }
62  fn size_hint(&self) -> Option<usize> {
63    Some(self.0.len())
64  }
65}
66
67struct MA<'de>(Peekable<toml::map::Iter<'de>>);
68
69impl<'de> MapAccess<'de> for MA<'de> {
70  type Error = Error;
71  fn next_key_seed<K: DeserializeSeed<'de>>
72    (&mut self, seed: K) -> Result<Option<K::Value>, Error>
73  {
74    Ok(if let Some((k, _v)) = self.0.peek() {
75      Some(str_deserialize(seed, k)?)
76    } else {
77      None
78    })
79  }
80  #[throws(Error)]
81  fn next_value_seed<V: DeserializeSeed<'de>>
82    (&mut self, seed: V) -> V::Value
83  {
84    let (_k, v) = self.0.next().unwrap();
85    seed.deserialize(TomlDe(v))?
86  }
87}
88
89struct EA<'de> { k: &'de str, v: &'de toml::Value }
90
91impl<'de> EnumAccess<'de> for EA<'de> {
92  type Error = Error;
93  type Variant = TomlDe<'de>;
94  #[throws(Error)]
95  fn variant_seed<V: DeserializeSeed<'de>>
96    (self, seed: V) -> (V::Value, TomlDe<'de>)
97  {
98    (str_deserialize(seed, self.k)?,
99     TomlDe(self.v))
100  }
101}
102
103impl<'de> VariantAccess<'de> for TomlDe<'de> {
104  type Error = Error;
105  #[throws(Error)]
106  fn unit_variant(self) { }
107
108  #[throws(Error)]
109  fn newtype_variant_seed<S: DeserializeSeed<'de>>
110    (self, seed: S) -> S::Value
111  {
112    seed.deserialize(self)?
113  }
114
115  #[throws(Error)]
116  fn tuple_variant<V: Visitor<'de>>(self, _: usize, v: V) -> V::Value {
117    visit(v, self.0)?
118  }
119
120  #[throws(Error)]
121  fn struct_variant<V: Visitor<'de>>(self, _:&[&str], v: V) -> V::Value {
122    visit(v, self.0)?
123  }
124}
125
126#[throws(Error)]
127fn visit<'de, V: Visitor<'de>>(v: V, tv: &'de toml::Value) -> V::Value {
128  type TV = toml::Value;
129  match tv {
130    TV::String(s) => v.visit_borrowed_str::<Error>(s)?,
131    &TV::Integer(i) => v.visit_i64::<Error>(i)?,
132    &TV::Float(f) => v.visit_f64::<Error>(f)?,
133    &TV::Boolean(b) => v.visit_bool::<Error>(b)?,
134    TV::Datetime(dt) => v.visit_str::<Error>(&dt.to_string())?,
135    TV::Array(a) => v.visit_seq(SA(a.as_slice().iter()))?,
136    TV::Table(t) => v.visit_map(MA(t.iter().peekable()))?,
137  }
138}
139
140impl<'de> Deserializer<'de> for TomlDe<'de> {
141  type Error = Error;
142  #[throws(Error)]
143  fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> V::Value {
144    visit(visitor, self.0)?
145  }
146  #[throws(Error)]
147  fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> V::Value {
148    // Option only works in structs, where it's represented by an
149    // absence of the key.  When we are called, we are either in a
150    // non-working context, or a context where we've already had
151    // the relevant struct key.
152    visitor.visit_some(self)?
153  }
154  #[throws(Error)]
155  fn deserialize_enum<V: Visitor<'de>>
156    (self, _:&str, _:&[&str], vi: V) -> V::Value
157  {
158    type TV = toml::Value;
159    match &self.0 {
160      TV::String(s) => return vi.visit_enum(s.as_str().into_deserializer())?,
161      TV::Table(s) => if_chain! {
162        let mut s = s.iter();
163        if let Some((k, v)) = s.next();
164        if let None = s.next();
165        then { return vi.visit_enum(EA { k, v }) }
166      },
167      _ => {}
168    }
169    // hopefully the format will figure it out, or produce an error
170    visit(vi, self.0)?
171  }
172  forward_to_deserialize_any! {
173    bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
174    bytes byte_buf unit unit_struct newtype_struct seq tuple
175    tuple_struct map struct identifier ignored_any
176  }
177}
178
179#[throws(Error)]
180pub fn from_value<'de, T: Deserialize<'de>>(tv: &'de toml::Value) -> T {
181  Deserialize::deserialize(TomlDe(tv))?
182}
183
184#[throws(Error)]
185pub fn from_str<T: DeserializeOwned>(s: &str) -> T {
186  let tv: toml::Value = s.parse().map_err(Error::TomlSyntax)?;
187//  dbg!(&tv);
188  from_value(&tv)?
189}
190
191#[throws(Error)]
192pub fn from_slice<T: DeserializeOwned>(s: &[u8]) -> T {
193  let tv: toml::Value = toml::de::from_slice(s).map_err(Error::TomlSyntax)?;
194//  dbg!(&tv);
195  from_value(&tv)?
196}