serde_java_properties/de/
mod.rs1use encoding_rs::{Encoding, UTF_8};
4use java_properties::LineContent::{Comment, KVPair};
5use java_properties::PropertiesIter;
6use serde::de::{self, IntoDeserializer, MapAccess, Visitor};
7use serde::forward_to_deserialize_any;
8use std::fmt;
9use std::io;
10use std::num::{ParseFloatError, ParseIntError};
11use std::str::ParseBoolError;
12
13mod field;
14
15pub struct Deserializer<R: io::Read> {
21 inner: PropertiesIter<R>,
22}
23
24impl<R: io::Read> Deserializer<R> {
25 pub fn from_reader(reader: R) -> Self {
31 Self {
32 inner: PropertiesIter::new(reader),
33 }
34 }
35
36 pub fn from_reader_with_encoding(reader: R, encoding: &'static Encoding) -> Self {
38 Self {
39 inner: PropertiesIter::new_with_encoding(reader, encoding),
40 }
41 }
42}
43
44impl<'a> Deserializer<io::Cursor<&'a str>> {
45 #[allow(clippy::should_implement_trait)]
47 pub fn from_str(s: &'a str) -> Self {
48 Self::from_reader_with_encoding(io::Cursor::new(s), UTF_8)
49 }
50}
51
52impl<'a> Deserializer<io::Cursor<&'a [u8]>> {
53 pub fn from_slice(s: &'a [u8]) -> Self {
59 Self::from_reader(io::Cursor::new(s))
60 }
61
62 pub fn from_slice_with_encoding(s: &'a [u8], encoding: &'static Encoding) -> Self {
64 Self::from_reader_with_encoding(io::Cursor::new(s), encoding)
65 }
66}
67
68#[derive(Debug)]
69#[non_exhaustive]
70pub enum Error {
72 Custom {
74 msg: String,
76 },
77 Properties(java_properties::PropertiesError),
79 ParseIntError(ParseIntError),
81 ParseFloatError(ParseFloatError),
83 ParseBoolError(ParseBoolError),
85 NotSupported,
87}
88
89impl From<java_properties::PropertiesError> for Error {
90 fn from(e: java_properties::PropertiesError) -> Self {
91 Self::Properties(e)
92 }
93}
94
95impl From<ParseIntError> for Error {
96 fn from(e: ParseIntError) -> Self {
97 Self::ParseIntError(e)
98 }
99}
100
101impl From<ParseFloatError> for Error {
102 fn from(e: ParseFloatError) -> Self {
103 Self::ParseFloatError(e)
104 }
105}
106
107impl From<ParseBoolError> for Error {
108 fn from(e: ParseBoolError) -> Self {
109 Self::ParseBoolError(e)
110 }
111}
112
113impl fmt::Display for Error {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 Self::Custom { msg } => write!(f, "Custom: {:?}", msg),
117 Self::NotSupported => write!(f, "Not supported"),
118 Self::Properties(e) => e.fmt(f),
119 Self::ParseIntError(e) => e.fmt(f),
120 Self::ParseFloatError(e) => e.fmt(f),
121 Self::ParseBoolError(e) => e.fmt(f),
122 }
123 }
124}
125
126impl std::error::Error for Error {}
127
128impl serde::de::Error for Error {
129 fn custom<T>(msg: T) -> Self
130 where
131 T: std::fmt::Display,
132 {
133 Self::Custom {
134 msg: msg.to_string(),
135 }
136 }
137}
138
139impl<'de, I: io::Read> de::Deserializer<'de> for Deserializer<I> {
140 type Error = Error;
141
142 fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
143 where
144 V: Visitor<'de>,
145 {
146 visitor.visit_map(PropertiesMapAccess {
147 de: self,
148 line_value: None,
149 })
150 }
151
152 forward_to_deserialize_any! {
153 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
154 bytes byte_buf option unit unit_struct newtype_struct seq tuple
155 tuple_struct map struct enum identifier ignored_any
156 }
157}
158
159struct PropertiesMapAccess<I: io::Read> {
160 de: Deserializer<I>,
161 line_value: Option<String>,
162}
163
164impl<'de, I: io::Read> MapAccess<'de> for PropertiesMapAccess<I> {
165 type Error = Error;
166
167 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
168 where
169 K: serde::de::DeserializeSeed<'de>,
170 {
171 while let Some(line) = self.de.inner.next().transpose()? {
172 match line.consume_content() {
173 Comment(_) => {} KVPair(key, value) => {
175 self.line_value = Some(value);
176 return seed.deserialize(key.into_deserializer()).map(Some);
177 }
178 };
179 }
180 Ok(None)
181 }
182
183 fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
184 where
185 V: serde::de::DeserializeSeed<'de>,
186 {
187 let value = self.line_value.take().unwrap();
188 seed.deserialize(field::FieldDeserializer(value))
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use serde::Deserialize;
195
196 use crate::de::Deserializer;
197
198 #[derive(Debug, Clone, PartialEq, Deserialize)]
199 struct Workload {
200 recordcount: usize,
201 operationcount: usize,
202 workload: String,
203
204 readallfields: bool,
205
206 readproportion: f32,
207 updateproportion: f32,
208 scanproportion: f32,
209 insertproportion: f32,
210
211 requestdistribution: String,
212
213 optional_string: Option<String>,
214 optional_usize: Option<usize>,
215 optional_bool: Option<bool>,
216 optional_string_empty: Option<String>,
217 optional_usize_empty: Option<usize>,
218 optional_bool_empty: Option<bool>,
219 optional_string_not_set: Option<String>,
220 optional_usize_not_set: Option<usize>,
221 optional_bool_not_set: Option<bool>,
222 }
223
224 #[test]
225 fn test() {
226 let data = "
227recordcount=1000
228operationcount=1000
229workload=site.ycsb.workloads.CoreWorkload
230
231readallfields=true
232
233readproportion=0.5
234updateproportion=0.5
235scanproportion=0
236insertproportion=0
237
238requestdistribution=zipfian
239
240optional_string=Hello, world!
241optional_usize=42
242optional_bool=true
243optional_string_empty=
244optional_usize_empty=
245optional_bool_empty=
246";
247 let deserializer = Deserializer::from_str(data);
248 let workload_a = Workload::deserialize(deserializer).unwrap();
249 assert_eq!(
250 workload_a,
251 Workload {
252 recordcount: 1000,
253 operationcount: 1000,
254 workload: "site.ycsb.workloads.CoreWorkload".to_string(),
255
256 readallfields: true,
257
258 readproportion: 0.5,
259 updateproportion: 0.5,
260 scanproportion: 0.0,
261 insertproportion: 0.0,
262
263 requestdistribution: "zipfian".to_string(),
264
265 optional_string: Some("Hello, world!".to_string()),
266 optional_usize: Some(42),
267 optional_bool: Some(true),
268 optional_string_empty: None,
269 optional_usize_empty: None,
270 optional_bool_empty: None,
271 optional_string_not_set: None,
272 optional_usize_not_set: None,
273 optional_bool_not_set: None
274 }
275 );
276 }
277}