foundationdb_gen/
lib.rs

1extern crate xml;
2
3use std::fmt;
4use xml::attribute::OwnedAttribute;
5use xml::reader::{EventReader, XmlEvent};
6
7const TAB1: &str = "    ";
8const TAB2: &str = "        ";
9const TAB3: &str = "            ";
10const TAB4: &str = "                ";
11
12#[derive(Debug)]
13struct FdbScope {
14    name: String,
15    options: Vec<FdbOption>,
16}
17impl FdbScope {
18    fn gen_ty<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
19        let with_ty = self.with_ty();
20
21        if with_ty {
22            writeln!(w, "#[derive(Clone, Debug)]")?;
23        } else {
24            writeln!(w, "#[derive(Clone, Copy, Debug)]")?;
25        }
26        writeln!(w, "#[non_exhaustive]")?;
27        writeln!(w, "pub enum {name} {{", name = self.name)?;
28
29        let with_ty = self.with_ty();
30        for option in self.options.iter() {
31            option.gen_ty(w, with_ty)?;
32        }
33        writeln!(w, "}}")
34    }
35
36    fn gen_impl<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
37        writeln!(w, "impl {name} {{", name = self.name)?;
38        self.gen_code(w)?;
39        self.gen_apply(w)?;
40        writeln!(w, "}}")
41    }
42
43    fn gen_code<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
44        writeln!(
45            w,
46            "{t}pub fn code(&self) -> fdb_sys::FDB{name} {{",
47            t = TAB1,
48            name = self.name,
49        )?;
50        writeln!(w, "{t}match *self {{", t = TAB2)?;
51
52        let enum_prefix = self.c_enum_prefix();
53        let with_ty = self.with_ty();
54
55        for option in self.options.iter() {
56            writeln!(
57                w,
58                "{t}{scope}::{name}{param} => fdb_sys::{enum_prefix}{code},",
59                t = TAB3,
60                scope = self.name,
61                name = option.name,
62                param = if let (true, Some(..)) = (with_ty, option.get_ty()) {
63                    "(..)"
64                } else {
65                    ""
66                },
67                enum_prefix = enum_prefix,
68                code = option.c_name,
69            )?;
70        }
71
72        writeln!(w, "{t}}}", t = TAB2)?;
73        writeln!(w, "{t}}}", t = TAB1)
74    }
75
76    fn gen_apply<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
77        let fn_name = match self.apply_fn_name() {
78            Some(name) => name,
79            _ => return Ok(()),
80        };
81
82        let first_arg = match self.apply_arg_name() {
83            Some(name) => format!(", target: *mut fdb_sys::{}", name),
84            None => String::new(),
85        };
86
87        writeln!(
88            w,
89            "{t}pub unsafe fn apply(&self{args}) -> FdbResult<()> {{",
90            t = TAB1,
91            args = first_arg
92        )?;
93        writeln!(w, "{t}let code = self.code();", t = TAB2)?;
94        writeln!(w, "{t}let err = match *self {{", t = TAB2)?;
95
96        let args = if first_arg.is_empty() {
97            "code"
98        } else {
99            "target, code"
100        };
101
102        for option in self.options.iter() {
103            write!(w, "{}{}::{}", TAB3, self.name, option.name)?;
104            match option.param_type {
105                FdbOptionTy::Empty => {
106                    writeln!(
107                        w,
108                        " => fdb_sys::{}({}, std::ptr::null(), 0),",
109                        fn_name, args
110                    )?;
111                }
112                FdbOptionTy::Int => {
113                    writeln!(w, "(v) => {{")?;
114                    writeln!(
115                        w,
116                        "{}let data: [u8;8] = std::mem::transmute(v as i64);",
117                        TAB4,
118                    )?;
119                    writeln!(
120                        w,
121                        "{}fdb_sys::{}({}, data.as_ptr() as *const u8, 8)",
122                        TAB4, fn_name, args
123                    )?;
124                    writeln!(w, "{t}}}", t = TAB3)?;
125                }
126                FdbOptionTy::Bytes => {
127                    writeln!(w, "(ref v) => {{")?;
128                    writeln!(
129                        w,
130                        "{}fdb_sys::{}({}, v.as_ptr() as *const u8, \
131                         i32::try_from(v.len()).expect(\"len to fit in i32\"))\n",
132                        TAB4, fn_name, args
133                    )?;
134                    writeln!(w, "{t}}}", t = TAB3)?;
135                }
136                FdbOptionTy::Str => {
137                    writeln!(w, "(ref v) => {{")?;
138                    writeln!(
139                        w,
140                        "{}fdb_sys::{}({}, v.as_ptr() as *const u8, \
141                         i32::try_from(v.len()).expect(\"len to fit in i32\"))\n",
142                        TAB4, fn_name, args
143                    )?;
144                    writeln!(w, "{t}}}", t = TAB3)?;
145                }
146            }
147        }
148
149        writeln!(w, "{t}}};", t = TAB2)?;
150        writeln!(
151            w,
152            "{t}if err != 0 {{ Err(FdbError::from_code(err)) }} else {{ Ok(()) }}",
153            t = TAB2,
154        )?;
155        writeln!(w, "{t}}}", t = TAB1)
156    }
157
158    fn with_ty(&self) -> bool {
159        self.apply_fn_name().is_some()
160    }
161
162    fn c_enum_prefix(&self) -> &'static str {
163        match self.name.as_str() {
164            "NetworkOption" => "FDBNetworkOption_FDB_NET_OPTION_",
165            "ClusterOption" => "FDBClusterOption_FDB_CLUSTER_OPTION_",
166            "DatabaseOption" => "FDBDatabaseOption_FDB_DB_OPTION_",
167            "TransactionOption" => "FDBTransactionOption_FDB_TR_OPTION_",
168            "StreamingMode" => "FDBStreamingMode_FDB_STREAMING_MODE_",
169            "MutationType" => "FDBMutationType_FDB_MUTATION_TYPE_",
170            "ConflictRangeType" => "FDBConflictRangeType_FDB_CONFLICT_RANGE_TYPE_",
171            "ErrorPredicate" => "FDBErrorPredicate_FDB_ERROR_PREDICATE_",
172            ty => panic!("unknown Scope name: `{}`", ty),
173        }
174    }
175
176    fn apply_arg_name(&self) -> Option<&'static str> {
177        let s = match self.name.as_str() {
178            "ClusterOption" => "FDBCluster",
179            "DatabaseOption" => "FDBDatabase",
180            "TransactionOption" => "FDBTransaction",
181            _ => return None,
182        };
183        Some(s)
184    }
185
186    fn apply_fn_name(&self) -> Option<&'static str> {
187        let s = match self.name.as_str() {
188            "NetworkOption" => "fdb_network_set_option",
189            "ClusterOption" => "fdb_cluster_set_option",
190            "DatabaseOption" => "fdb_database_set_option",
191            "TransactionOption" => "fdb_transaction_set_option",
192            _ => return None,
193        };
194        Some(s)
195    }
196}
197
198#[derive(Clone, Copy, Debug, Default)]
199enum FdbOptionTy {
200    #[default]
201    Empty,
202    Int,
203    Str,
204    Bytes,
205}
206
207#[derive(Default, Debug)]
208struct FdbOption {
209    name: String,
210    c_name: String,
211    code: i32,
212    param_type: FdbOptionTy,
213    param_description: String,
214    description: String,
215    hidden: bool,
216    default_for: Option<i32>,
217    persistent: bool,
218    sensitive: bool,
219}
220
221impl FdbOption {
222    fn gen_ty<W: fmt::Write>(&self, w: &mut W, with_ty: bool) -> fmt::Result {
223        if !self.param_description.is_empty() {
224            writeln!(w, "{t}/// {desc}", t = TAB1, desc = self.param_description)?;
225            writeln!(w, "{t}///", t = TAB1)?;
226        }
227        if !self.description.is_empty() {
228            writeln!(w, "{t}/// {desc}", t = TAB1, desc = self.description)?;
229        }
230
231        if let (true, Some(ty)) = (with_ty, self.get_ty()) {
232            writeln!(w, "{t}{name}({ty}),", t = TAB1, name = self.name, ty = ty)?;
233        } else {
234            writeln!(w, "{t}{name},", t = TAB1, name = self.name)?;
235        }
236        Ok(())
237    }
238
239    fn get_ty(&self) -> Option<&'static str> {
240        match self.param_type {
241            FdbOptionTy::Int => Some("i32"),
242            FdbOptionTy::Str => Some("String"),
243            FdbOptionTy::Bytes => Some("Vec<u8>"),
244            FdbOptionTy::Empty => None,
245        }
246    }
247}
248
249fn to_rs_enum_name(v: &str) -> String {
250    let mut is_start_of_word = true;
251    v.chars()
252        .filter_map(|c| {
253            if c == '_' {
254                is_start_of_word = true;
255                None
256            } else if is_start_of_word {
257                is_start_of_word = false;
258                Some(c.to_ascii_uppercase())
259            } else {
260                Some(c)
261            }
262        })
263        .collect()
264}
265
266impl From<Vec<OwnedAttribute>> for FdbOption {
267    fn from(attrs: Vec<OwnedAttribute>) -> Self {
268        let mut opt = Self::default();
269        for attr in attrs {
270            let v = attr.value;
271            match attr.name.local_name.as_str() {
272                "name" => {
273                    opt.name = to_rs_enum_name(v.as_str());
274                    opt.c_name = v.to_uppercase();
275                }
276                "code" => {
277                    opt.code = v.parse().expect("code to be a i32");
278                }
279                "paramType" => {
280                    opt.param_type = match v.as_str() {
281                        "Int" => FdbOptionTy::Int,
282                        "String" => FdbOptionTy::Str,
283                        "Bytes" => FdbOptionTy::Bytes,
284                        "" => FdbOptionTy::Empty,
285                        ty => panic!("unexpected param_type: {}", ty),
286                    };
287                }
288                "paramDescription" => {
289                    opt.param_description = v;
290                }
291                "description" => {
292                    opt.description = v;
293                }
294                "hidden" => match v.as_str() {
295                    "true" => opt.hidden = true,
296                    "false" => opt.hidden = false,
297                    _ => panic!("unexpected boolean value in 'hidden': {}", v),
298                },
299                "defaultFor" => {
300                    opt.default_for = Some(v.parse().expect("defaultFor to be a i32"));
301                }
302                "persistent" => match v.as_str() {
303                    "true" => opt.persistent = true,
304                    "false" => opt.persistent = false,
305                    _ => panic!("unexpected boolean value in 'persistent': {}", v),
306                },
307                "sensitive" => match v.as_str() {
308                    "true" => opt.sensitive = true,
309                    "false" => opt.sensitive = false,
310                    _ => panic!("unexpected boolean value in 'sensitive': {}", v),
311                },
312                attr => {
313                    panic!("unexpected option attribute: {}", attr);
314                }
315            }
316        }
317        opt
318    }
319}
320
321fn on_scope<I>(parser: &mut I) -> Vec<FdbOption>
322where
323    I: Iterator<Item = xml::reader::Result<XmlEvent>>,
324{
325    let mut options = Vec::new();
326    for e in parser {
327        let e = e.unwrap();
328        match e {
329            XmlEvent::StartElement {
330                name, attributes, ..
331            } => {
332                assert_eq!(name.local_name, "Option", "unexpected token");
333
334                let option = FdbOption::from(attributes.clone());
335                if !option.hidden {
336                    options.push(option);
337                }
338            }
339            XmlEvent::EndElement { name, .. } => {
340                if name.local_name == "Scope" {
341                    return options;
342                }
343            }
344            _ => {}
345        }
346    }
347
348    panic!("unexpected end of token");
349}
350
351#[cfg(all(not(feature = "embedded-fdb-include"), target_os = "linux"))]
352const OPTIONS_DATA: &[u8] = include_bytes!("/usr/include/foundationdb/fdb.options");
353
354#[cfg(all(not(feature = "embedded-fdb-include"), target_os = "macos"))]
355const OPTIONS_DATA: &[u8] = include_bytes!("/usr/local/include/foundationdb/fdb.options");
356
357#[cfg(all(not(feature = "embedded-fdb-include"), target_os = "windows"))]
358const OPTIONS_DATA: &[u8] =
359    include_bytes!("C:/Program Files/foundationdb/include/foundationdb/fdb.options");
360
361#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-5_1"))]
362const OPTIONS_DATA: &[u8] = include_bytes!("../include/510/fdb.options");
363#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-5_2"))]
364const OPTIONS_DATA: &[u8] = include_bytes!("../include/520/fdb.options");
365#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-6_0"))]
366const OPTIONS_DATA: &[u8] = include_bytes!("../include/600/fdb.options");
367#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-6_1"))]
368const OPTIONS_DATA: &[u8] = include_bytes!("../include/610/fdb.options");
369#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-6_2"))]
370const OPTIONS_DATA: &[u8] = include_bytes!("../include/620/fdb.options");
371#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-6_3"))]
372const OPTIONS_DATA: &[u8] = include_bytes!("../include/630/fdb.options");
373#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-7_0"))]
374const OPTIONS_DATA: &[u8] = include_bytes!("../include/700/fdb.options");
375#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-7_1"))]
376const OPTIONS_DATA: &[u8] = include_bytes!("../include/710/fdb.options");
377#[cfg(all(feature = "embedded-fdb-include", feature = "fdb-7_3"))]
378const OPTIONS_DATA: &[u8] = include_bytes!("../include/730/fdb.options");
379
380pub fn emit(w: &mut impl fmt::Write) -> fmt::Result {
381    let mut reader = OPTIONS_DATA;
382    let parser = EventReader::new(&mut reader);
383    let mut iter = parser.into_iter();
384    let mut scopes = Vec::new();
385
386    while let Some(e) = iter.next() {
387        match e.unwrap() {
388            XmlEvent::StartElement {
389                name, attributes, ..
390            } => {
391                if name.local_name == "Scope" {
392                    let scope_name = attributes
393                        .into_iter()
394                        .find(|attr| attr.name.local_name == "name")
395                        .unwrap();
396
397                    let options = on_scope(&mut iter);
398                    scopes.push(FdbScope {
399                        name: scope_name.value,
400                        options,
401                    });
402                }
403            }
404            XmlEvent::EndElement { .. } => {
405                //
406            }
407            _ => {}
408        }
409    }
410
411    writeln!(w, "use std::convert::TryFrom;")?;
412    writeln!(w, "use crate::{{FdbError, FdbResult}};")?;
413    writeln!(w, "use foundationdb_sys as fdb_sys;")?;
414    for scope in scopes.iter() {
415        scope.gen_ty(w)?;
416        scope.gen_impl(w)?;
417    }
418
419    Ok(())
420}