1#![feature(proc_macro_span)]
2
3extern crate proc_macro;
4use proc_macro::{token_stream::IntoIter, TokenStream};
5use std::collections::HashMap;
6use std::fmt::Write;
7use std::iter::IntoIterator;
8
9#[derive(Debug)]
10struct Cls {
11 cls: String,
12 map: HashMap<String, String>,
13}
14
15impl Cls {
16 fn new(t: &impl ToString) -> Self {
17 Cls {
18 cls: t.to_string(),
19 map: HashMap::new(),
20 }
21 }
22}
23
24fn parse(mut iter: IntoIter) -> Vec<Cls> {
25 let mut vec = vec![];
26 if let Some(t) = iter.next() {
27 let mut cls = Cls::new(&t);
28 let span = t.span().start();
29 let mut line = span.line;
30 let mut indent_base = span.column;
31 let mut key = String::new();
32 let mut is_key = true;
33 for t in iter {
34 let span = t.span().start();
35 if span.line != line {
36 line = span.line;
37 let column = span.column;
38 if column <= indent_base {
39 vec.push(cls);
40 cls = Cls::new(&t);
41 indent_base = column;
42 is_key = false;
43 } else {
44 key = t.to_string();
45 cls.map.insert(key.clone(), "".into());
46 is_key = true;
47 }
48 } else if is_key {
49 cls.map.insert(
50 key.clone(),
51 cls.map.get(&key).unwrap().to_owned() + &t.to_string(),
52 );
53 }
54 }
55 vec.push(cls);
56 }
57 vec
58}
59
60const ONE: &str = "One";
61const DUP: &str = "Dup";
62
63const LITTLE_ENDIAN: bool = cfg!(target_endian = "little");
64
65macro_rules! _static {
66 ($x:ident) => {
67 if ["Bin", "Str"].contains(&$x.as_str()) {
68 $x = format!("{}<'static>", $x);
69 }
70 };
71}
72
73#[proc_macro]
74pub fn mdbx(input: TokenStream) -> TokenStream {
75 let env;
76 let mut iter = input.into_iter();
77 let mut code = String::new();
78
79 if let Some(t) = iter.next() {
80 env = t.to_string();
81 let vec = parse(iter);
82 for i in vec {
83 let map = i.map;
84
85 macro_rules! get {
86 ($key: ident) => {
87 get!($key, "mdbx::r#type::Bin<'static>")
88 };
89 ($key: ident, $default: expr) => {
90 map
91 .get(stringify!($key))
92 .map_or($default, String::as_str)
93 .to_string()
94 };
95 }
96
97 let mut key = get!(key);
98 let mut val = get!(val);
99
100 let flag = &get!(flag, "DB_DEFAULTS");
101 let mut flag = flag.split('|').collect::<Vec<&str>>();
102
103 let kind = if flag.contains(&"DUPSORT") { DUP } else { ONE };
104
105 if key == "u32" || key == "u64" || key == "usize" {
106 flag.push("INTEGERKEY");
107 } else if ["u16", "u128", "i16", "i32", "i64", "i128", "isize"].contains(&key.as_str()) {
108 if LITTLE_ENDIAN {
109 flag.push("REVERSEKEY");
110 }
111 } else {
112 _static!(key)
113 };
114
115 _static!(val);
116
117 if kind == DUP
118 && [
119 "usize", "u128", "u64", "u32", "u16", "u8", "isize", "i128", "i64", "i32", "i16", "i8",
120 ]
121 .contains(&val.as_str())
122 {
123 flag.push("DUPFIXED");
124 if val == "u32" || val == "u64" || val == "usize" {
125 flag.push("INTEGERDUP")
126 } else if LITTLE_ENDIAN {
127 flag.push("REVERSEDUP");
128 }
129 }
130
131 flag.sort_unstable();
132 flag.dedup();
133
134 let flag = flag
135 .into_iter()
136 .map(|x| format!("mdbx::flag::DB::MDBX_{x}"))
137 .collect::<Vec<String>>()
138 .join("|");
139
140 writeln!(
143 &mut code,
144 "mdbx::Db!({env},{},mdbx::db::kind::{kind},{key},{val},{flag});\n",
145 i.cls
146 )
147 .unwrap();
148 }
149 }
150
151 let code: TokenStream = code.parse().unwrap();
153 code
154}