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 }
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}