chiark / gitweb /
changelog: document further make-release changes
[otter.git] / 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
5 use crate::crates::*;
6 use otter_base::crates::*;
7
8 use std::fmt::{Debug, Display};
9 use std::iter::Peekable;
10 use std::slice;
11
12 use fehler::throws;
13 use if_chain::if_chain;
14 use thiserror::Error;
15
16 use serde::forward_to_deserialize_any;
17 use serde::de::{
18   Deserialize, DeserializeOwned, Deserializer,
19   DeserializeSeed, EnumAccess,
20   IntoDeserializer, MapAccess,
21   SeqAccess, VariantAccess, Visitor
22 };
23
24 #[derive(Error,Debug)]
25 pub 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
32 impl 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
38 fn 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
46 pub struct TomlDe<'de>(pub &'de toml::Value);
47
48 struct SA<'de>(slice::Iter<'de, toml::Value>);
49
50 impl<'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
67 struct MA<'de>(Peekable<toml::map::Iter<'de>>);
68
69 impl<'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
89 struct EA<'de> { k: &'de str, v: &'de toml::Value }
90
91 impl<'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
103 impl<'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)]
127 fn 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
140 impl<'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)]
180 pub fn from_value<'de, T: Deserialize<'de>>(tv: &'de toml::Value) -> T {
181   Deserialize::deserialize(TomlDe(tv))?
182 }
183
184 #[throws(Error)]
185 pub 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)]
192 pub 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 }