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