1use std::cmp::Ordering;
16use std::fmt::Debug;
17use std::fmt::Display;
18use std::hash::Hash;
19use std::hash::Hasher;
20use std::io::Cursor;
21use std::io::Read;
22use std::marker::PhantomData;
23use std::ops::Deref;
24use std::ops::DerefMut;
25use std::path::Path;
26use std::path::PathBuf;
27use std::str::FromStr;
28
29use serde::Deserialize;
30
31pub trait DeserializeReader<T> {
32 type Error;
33
34 fn deserialize<R: Read>(reader: R) -> Result<T, Self::Error>;
35}
36
37pub struct JsonFormat;
39
40impl<'de, T> DeserializeReader<T> for JsonFormat
41where
42 T: Deserialize<'de>,
43{
44 type Error = serde_json::Error;
45
46 fn deserialize<R: Read>(reader: R) -> Result<T, Self::Error> {
47 let mut deser = serde_json::Deserializer::from_reader(reader);
48 T::deserialize(&mut deser)
49 }
50}
51
52pub struct TomlFormat;
54
55impl<T> DeserializeReader<T> for TomlFormat
56where
57 T: for<'de> Deserialize<'de>,
58{
59 type Error = std::io::Error;
60
61 fn deserialize<R: Read>(reader: R) -> Result<T, Self::Error> {
62 let str = std::io::read_to_string(reader)?;
64 toml::from_str(&str)
65 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
66 }
67}
68
69pub struct Serde<T, D>(T, PhantomData<D>);
73
74pub type Json<T> = Serde<T, JsonFormat>;
75pub type Toml<T> = Serde<T, TomlFormat>;
76
77impl<'de, T, D> FromStr for Serde<T, D>
78where
79 T: Deserialize<'de>,
80 D: DeserializeReader<T>,
81{
82 type Err = D::Error;
83
84 fn from_str(s: &str) -> Result<Self, D::Error> {
85 D::deserialize(Cursor::new(s)).map(|v| Self(v, PhantomData))
86 }
87}
88
89impl<T, D> Deref for Serde<T, D> {
90 type Target = T;
91
92 #[inline]
93 fn deref(&self) -> &Self::Target {
94 &self.0
95 }
96}
97
98impl<T, D> DerefMut for Serde<T, D> {
99 #[inline]
100 fn deref_mut(&mut self) -> &mut Self::Target {
101 &mut self.0
102 }
103}
104
105impl<T, D> Debug for Serde<T, D>
106where
107 T: Debug,
108{
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 Debug::fmt(&self.0, f)
111 }
112}
113
114impl<T, D> Clone for Serde<T, D>
115where
116 T: Clone,
117{
118 fn clone(&self) -> Self {
119 Self(self.0.clone(), PhantomData)
120 }
121}
122
123impl<T, D> Serde<T, D> {
124 #[inline]
125 pub fn into_inner(self) -> T {
126 self.0
127 }
128
129 #[inline]
130 pub fn as_inner(&self) -> &T {
131 &self.0
132 }
133}
134
135pub struct SerdeFile<T, D> {
139 value: T,
140 path: PathBuf,
141 deser: PhantomData<D>,
142}
143
144pub type JsonFile<T> = SerdeFile<T, JsonFormat>;
145pub type TomlFile<T> = SerdeFile<T, TomlFormat>;
146
147impl<'de, T, D> FromStr for SerdeFile<T, D>
148where
149 T: Deserialize<'de>,
150 D: DeserializeReader<T>,
151 D::Error: Display,
152{
153 type Err = std::io::Error;
154
155 fn from_str(path: &str) -> std::io::Result<Self> {
156 let f = std::fs::File::open(path)?;
157 D::deserialize(f)
158 .map(|value| Self {
159 path: path.into(),
160 value,
161 deser: PhantomData,
162 })
163 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))
164 }
165}
166
167impl<T, D> Deref for SerdeFile<T, D> {
168 type Target = T;
169
170 #[inline]
171 fn deref(&self) -> &Self::Target {
172 &self.value
173 }
174}
175
176impl<T, D> DerefMut for SerdeFile<T, D> {
177 #[inline]
178 fn deref_mut(&mut self) -> &mut Self::Target {
179 &mut self.value
180 }
181}
182
183impl<T, D> Debug for SerdeFile<T, D>
184where
185 T: Debug,
186{
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 f.debug_struct("SerdeFile")
189 .field("path", &self.path)
190 .field("value", &self.value)
191 .finish()
192 }
193}
194
195impl<T, D> Clone for SerdeFile<T, D>
196where
197 T: Clone,
198{
199 fn clone(&self) -> Self {
200 Self {
201 path: self.path.clone(),
202 value: self.value.clone(),
203 deser: PhantomData,
204 }
205 }
206}
207
208impl<T, D> SerdeFile<T, D> {
209 #[inline]
210 pub fn path(&self) -> &Path {
211 &self.path
212 }
213
214 #[inline]
215 pub fn as_inner(&self) -> &T {
216 self
217 }
218
219 #[inline]
220 pub fn into_inner(self) -> T {
221 self.value
222 }
223}
224
225macro_rules! common_impl {
226 ($i:ident) => {
227 impl<T, D> AsRef<T> for $i<T, D> {
228 #[inline]
229 fn as_ref(&self) -> &T {
230 self
231 }
232 }
233
234 impl<T, D> PartialEq for $i<T, D>
235 where
236 T: PartialEq,
237 {
238 fn eq(&self, rhs: &Self) -> bool {
239 self.as_inner() == rhs.as_inner()
240 }
241 }
242
243 impl<T, D> PartialEq<T> for $i<T, D>
244 where
245 T: PartialEq,
246 {
247 fn eq(&self, rhs: &T) -> bool {
248 self.as_inner() == rhs
249 }
250 }
251
252 impl<T, D> Eq for $i<T, D> where T: Eq {}
253
254 impl<T, D> PartialOrd for $i<T, D>
255 where
256 T: PartialOrd,
257 {
258 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
259 self.as_inner().partial_cmp(other.as_inner())
260 }
261 }
262
263 impl<T, D> PartialOrd<T> for $i<T, D>
264 where
265 T: PartialOrd,
266 {
267 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
268 self.as_inner().partial_cmp(other)
269 }
270 }
271
272 impl<T, D> Ord for $i<T, D>
273 where
274 T: Ord,
275 {
276 fn cmp(&self, other: &Self) -> Ordering {
277 self.as_inner().cmp(other.as_inner())
278 }
279 }
280
281 impl<T, D> Hash for $i<T, D>
282 where
283 T: Hash,
284 {
285 fn hash<H>(&self, state: &mut H)
286 where
287 H: Hasher,
288 {
289 self.as_inner().hash(state)
290 }
291 }
292 };
293}
294
295common_impl!(Serde);
296common_impl!(SerdeFile);
297
298#[cfg(test)]
299mod tests {
300 use std::ffi::OsStr;
301
302 use clap::Parser;
303 use serde::Serialize;
304 use similar_asserts::assert_eq;
305 use tempfile::NamedTempFile;
306
307 use super::*;
308
309 #[derive(Debug, PartialEq, Deserialize, Serialize)]
310 struct Example {
311 foo: String,
312 bar: u32,
313 }
314
315 #[derive(Debug, Parser)]
316 struct Args {
317 #[clap(long)]
318 inline: Option<Json<Example>>,
319 #[clap(long)]
320 file: Option<JsonFile<Example>>,
321 }
322
323 #[test]
324 fn inline() {
325 let example = Example {
326 foo: "baz".into(),
327 bar: 42,
328 };
329 let inline_str = serde_json::to_string(&example).expect("failed to serialize");
330 let args = Args::parse_from(vec!["inline", "--inline", &inline_str]);
331 assert_eq!(args.inline.expect("definitely here"), example,);
332 }
333
334 #[test]
335 fn file() {
336 let example = Example {
337 foo: "baz".into(),
338 bar: 42,
339 };
340 let mut tmp = NamedTempFile::new().expect("failed to create tmp file");
341 serde_json::to_writer(&mut tmp, &example).expect("failed to serialize");
342 let args = Args::parse_from(vec![
343 OsStr::new("file"),
344 OsStr::new("--file"),
345 tmp.path().as_os_str(),
346 ]);
347 assert_eq!(args.file.expect("definitely here"), example);
348 }
349}