1use indexmap::IndexSet;
2use std::io::{BufReader, BufWriter, Read, Write};
3
4mod infer;
5mod util;
6pub use util::Wrapper;
7
8mod generate;
9use generate::{Print, Program};
10
11pub fn generate<R, W>(opts: Options, read: &mut R, write: &mut W) -> anyhow::Result<()>
12where
13 R: Read + ?Sized,
14 W: Write + ?Sized,
15{
16 let mut reader = BufReader::new(read);
17 let mut buf = String::new();
18 reader.read_to_string(&mut buf)?;
19
20 let program = Program::generate(json::parse(&buf)?, &buf, &opts);
21
22 let mut writer = BufWriter::new(write);
23 program.print(&mut writer, &opts)?;
24
25 Ok(())
26}
27
28#[derive(Debug)]
29pub struct Options {
30 pub json_name: Option<String>,
31 pub root_name: String,
32
33 pub make_unit_test: bool,
34 pub make_main: bool,
35
36 pub collapse_option_vec: bool,
37
38 pub tuple_max: Option<usize>,
39
40 pub default_derives: String,
41 pub field_naming: CasingScheme,
42 pub struct_naming: CasingScheme,
43
44 pub vec_wrapper: Wrapper,
45 pub map_wrapper: Wrapper,
46}
47
48#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
49pub enum CasingScheme {
50 Snake,
51 Pascal,
52 Constant,
53 Camel,
54 Identity,
55}
56
57impl CasingScheme {
58 fn convert(self, input: &str) -> String {
59 use inflections::Inflect as _;
60 match self {
61 Self::Snake => input.to_snake_case(),
62 Self::Pascal => input.to_pascal_case(),
63 Self::Constant => input.to_constant_case(),
64 Self::Camel => input.to_camel_case(),
65 Self::Identity => input.to_string(),
66 }
67 }
68}
69
70pub fn no_derives() -> String {
71 custom::<&str, _>(std::iter::empty())
72}
73
74pub fn all_std_derives() -> String {
75 custom(&[
76 "Clone",
77 "Debug",
78 "PartialEq",
79 "PartialOrd",
80 "Eq",
81 "Ord",
82 "Hash",
83 ])
84}
85
86pub fn custom<S, L>(list: L) -> String
87where
88 S: ToString,
89 L: IntoIterator<Item = S>,
90{
91 const DEFAULT: [&str; 2] = ["Serialize", "Deserialize"];
92
93 list.into_iter()
94 .flat_map(|s| {
95 s.to_string()
96 .split(',') .filter(|c| !c.starts_with(char::is_numeric)) .map(ToString::to_string)
99 .collect::<IndexSet<_>>() })
101 .filter(|s| !DEFAULT.contains(&&**s)) .chain(DEFAULT.iter().map(ToString::to_string)) .collect::<IndexSet<_>>() .into_iter()
105 .fold(String::new(), |mut a, c| {
106 if !a.is_empty() {
107 a.push_str(", ");
108 }
109 a.push_str(&c);
110 a
111 })
112}