1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::io;
4use std::rc::Rc;
5
6use crate::visitor;
7
8#[derive(Debug, PartialEq, serde::Deserialize)]
9struct ConfigSpec {
10 schema_version: u32,
11 #[serde(default)]
12 functions: Vec<FunctionSpec>,
13 #[serde(default)]
14 modules: Vec<ModuleSpec>,
15}
16
17#[derive(Debug, PartialEq, serde::Deserialize)]
18struct FunctionSpec {
19 pragma: String,
20}
21
22#[derive(Debug, PartialEq, serde::Deserialize)]
23struct ModuleSpec {
24 pragma: String,
25 #[serde(default)]
26 derives: Vec<ModuleDeriveSpec>,
27}
28
29#[derive(Debug, PartialEq, serde::Deserialize)]
30pub(crate) struct ModuleDeriveSpec {
31 pub(crate) name: Option<String>,
32 #[serde(default)]
33 pub(crate) modules: Vec<ModuleDeriveSpec>,
34 #[serde(default)]
35 pub(crate) functions: Vec<String>,
36}
37
38impl TryFrom<&str> for ConfigSpec {
39 type Error = serde_json::Error;
40
41 fn try_from(spec: &str) -> Result<Self, Self::Error> {
42 let spec: Self = serde_json::from_str(spec)?;
43 if spec.schema_version != crate::SCHEMA_VERSION {
44 return Err(serde_json::Error::io(io::Error::new(
45 io::ErrorKind::InvalidData,
46 format!(
47 "schema_version {} does not match supported version {}",
48 spec.schema_version,
49 crate::SCHEMA_VERSION
50 ),
51 )));
52 }
53
54 Ok(spec)
55 }
56}
57
58pub(crate) fn build_config_from_json_spec(spec: &str) -> Result<BuildConfig, serde_json::Error> {
59 let spec = ConfigSpec::try_from(spec)?;
60 Ok(BuildConfig::from_spec(&spec))
61}
62
63#[derive(Default, Debug)]
64pub struct BuildConfig {
65 pub(crate) pragmas: Vec<String>,
66 pub(crate) derives: Vec<visitor::CustomDerive>,
67 pub(crate) module_derives: HashMap<String, Vec<visitor::CustomModuleDerive>>,
68}
69
70impl BuildConfig {
71 pub(crate) fn merge(&mut self, other: Self) {
72 for pragma in other.pragmas {
73 push_unique_pragma(&mut self.pragmas, pragma.as_str());
74 }
75
76 self.derives.extend(other.derives);
77
78 for (pragma, derives) in other.module_derives {
79 self.module_derives
80 .entry(pragma)
81 .or_default()
82 .extend(derives);
83 }
84 }
85
86 pub fn pragma(&mut self, pragma: impl Into<String>) -> &mut Self {
88 self.pragmas.push(pragma.into());
89 self
90 }
91
92 pub fn derive(&mut self, name: impl Into<String>, template: impl AsRef<str>) -> &mut Self {
94 self.derives
95 .push(visitor::CustomDerive::new(name, template.as_ref()));
96 self
97 }
98
99 pub fn module_derive(
100 &mut self,
101 extension: impl Into<String>,
102 configure: impl FnOnce(&mut ModuleDeriveBuilder),
103 ) -> &mut Self {
104 let extension = extension.into();
105 let existing_derives = self
106 .module_derives
107 .get(&extension)
108 .cloned()
109 .unwrap_or_default();
110
111 let mut builder = ModuleDeriveBuilder::new(existing_derives);
112 configure(&mut builder);
113
114 self.module_derives
115 .insert(extension, builder.derives.borrow().clone());
116
117 self
118 }
119
120 fn from_spec(spec: &ConfigSpec) -> Self {
121 let mut config = BuildConfig::default();
122
123 for function in &spec.functions {
124 push_unique_pragma(&mut config.pragmas, &function.pragma);
125 }
126
127 for module in &spec.modules {
128 push_unique_pragma(&mut config.pragmas, &module.pragma);
129 config.module_derives.insert(
130 module.pragma.clone(),
131 module
132 .derives
133 .iter()
134 .map(custom_module_derive_from_spec)
135 .collect(),
136 );
137 }
138
139 config
140 }
141}
142
143#[derive(Default, Clone)]
144pub struct ModuleDeriveBuilder {
145 derives: Rc<RefCell<Vec<visitor::CustomModuleDerive>>>,
146 current_derive_path: Vec<usize>,
147}
148
149impl ModuleDeriveBuilder {
150 fn new(existing_derives: Vec<visitor::CustomModuleDerive>) -> Self {
151 Self {
152 derives: Rc::new(RefCell::new(existing_derives)),
153 current_derive_path: vec![],
154 }
155 }
156
157 pub fn module(&mut self, module: impl Into<String>) -> Self {
158 let mut clone = self.clone();
159
160 if clone.current_derive_path.is_empty() {
161 let index = clone.derives.borrow().len();
162 clone
163 .derives
164 .borrow_mut()
165 .push(visitor::CustomModuleDerive::new(module.into()));
166 clone.current_derive_path.push(index);
167 } else {
168 let mut index = 0;
169 clone.with_derive_at_path(|derive| {
170 index = derive.add_module(module.into());
171 });
172 clone.current_derive_path.push(index);
173 }
174
175 clone
176 }
177
178 pub fn functions(&mut self, functions: impl IntoIterator<Item = impl Into<String>>) -> Self {
179 let clone = self.clone();
180
181 clone.with_derive_at_path(|derive| {
182 for each in functions {
183 derive.add_function(each.into());
184 }
185 });
186
187 clone
188 }
189
190 fn with_derive_at_path(&self, f: impl FnOnce(&mut visitor::CustomModuleDerive)) {
191 if self.derives.borrow().is_empty() {
192 self.derives
193 .borrow_mut()
194 .push(visitor::CustomModuleDerive::default());
195 }
196
197 let mut borrow = self.derives.borrow_mut();
198 if self.current_derive_path.is_empty() {
199 let last_derive = borrow
200 .last_mut()
201 .unwrap()
202 .derive_at_mut(&self.current_derive_path);
203
204 f(last_derive)
205 } else {
206 let index = *self.current_derive_path.first().unwrap();
207 let first_derive = borrow.get_mut(index).unwrap();
208 f(first_derive.derive_at_mut(&self.current_derive_path[1..]));
209 }
210 }
211}
212
213fn push_unique_pragma(pragmas: &mut Vec<String>, pragma: &str) {
214 if pragmas.iter().all(|existing| existing != pragma) {
215 pragmas.push(pragma.to_string());
216 }
217}
218
219fn custom_module_derive_from_spec(spec: &ModuleDeriveSpec) -> visitor::CustomModuleDerive {
220 visitor::CustomModuleDerive::from_spec(spec)
221}
222
223#[macro_export]
224macro_rules! custom {
225 ($($tokens:tt)*) => {
226 $crate::__custom_config!(@parse [] [] $($tokens)*)
227 };
228}
229
230#[doc(hidden)]
231#[macro_export]
232macro_rules! __custom_config {
233 (@parse [ $($functions:expr,)* ] [ $($modules:expr,)* ]) => {
234 concat!(
235 "{",
236 "\"schema_version\":",
237 $crate::__custom_schema_version!(),
238 ",",
239 "\"functions\":[",
240 $crate::__custom_join!($($functions),*),
241 "],",
242 "\"modules\":[",
243 $crate::__custom_join!($($modules),*),
244 "]",
245 "}"
246 )
247 };
248 (@parse [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn pragma $pragma:literal, $($rest:tt)*) => {
249 $crate::__custom_config!(
250 @parse
251 [
252 $($functions,)*
253 concat!("{", "\"pragma\":", $crate::__custom_json_string_literal!($pragma), "}"),
254 ]
255 [ $($modules,)* ]
256 $($rest)*
257 )
258 };
259 (@parse [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn pragma $pragma:literal) => {
260 $crate::__custom_config!(
261 @parse
262 [
263 $($functions,)*
264 concat!("{", "\"pragma\":", $crate::__custom_json_string_literal!($pragma), "}"),
265 ]
266 [ $($modules,)* ]
267 )
268 };
269 (@parse [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod pragma $pragma:literal { $($body:tt)* }, $($rest:tt)*) => {
270 $crate::__custom_config!(
271 @parse
272 [ $($functions,)* ]
273 [
274 $($modules,)*
275 concat!(
276 "{",
277 "\"pragma\":",
278 $crate::__custom_json_string_literal!($pragma),
279 ",",
280 "\"derives\":[",
281 $crate::__custom_module_derives!(@parse [] $($body)*),
282 "]",
283 "}"
284 ),
285 ]
286 $($rest)*
287 )
288 };
289 (@parse [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod pragma $pragma:literal { $($body:tt)* }) => {
290 $crate::__custom_config!(
291 @parse
292 [ $($functions,)* ]
293 [
294 $($modules,)*
295 concat!(
296 "{",
297 "\"pragma\":",
298 $crate::__custom_json_string_literal!($pragma),
299 ",",
300 "\"derives\":[",
301 $crate::__custom_module_derives!(@parse [] $($body)*),
302 "]",
303 "}"
304 ),
305 ]
306 )
307 };
308}
309
310#[doc(hidden)]
311#[macro_export]
312macro_rules! __custom_join {
313 () => {
314 ""
315 };
316 ($value:expr) => {
317 $value
318 };
319 ($first:expr, $($rest:expr),+ $(,)?) => {
320 concat!($first, ",", $crate::__custom_join!($($rest),+))
321 };
322}
323
324#[doc(hidden)]
325#[macro_export]
326macro_rules! __custom_json_string_literal {
327 ($value:literal) => {
328 concat!("\"", $value, "\"")
329 };
330}
331
332#[doc(hidden)]
333#[macro_export]
334macro_rules! __custom_schema_version {
335 () => {
336 "1"
337 };
338}
339
340#[doc(hidden)]
341#[macro_export]
342macro_rules! __custom_module_derives {
343 (@parse [ $($derives:expr,)* ]) => {
344 $crate::__custom_join!($($derives),*)
345 };
346 (@parse [ $($derives:expr,)* ] derive { mod $name:ident { $($body:tt)* } }, $($rest:tt)*) => {
347 $crate::__custom_module_derives!(
348 @parse
349 [
350 $($derives,)*
351 $crate::__custom_module_derive_spec!(@named_ident $name [] [] $($body)*),
352 ]
353 $($rest)*
354 )
355 };
356 (@parse [ $($derives:expr,)* ] derive { mod $name:ident { $($body:tt)* } }) => {
357 $crate::__custom_module_derives!(
358 @parse
359 [
360 $($derives,)*
361 $crate::__custom_module_derive_spec!(@named_ident $name [] [] $($body)*),
362 ]
363 )
364 };
365 (@parse [ $($derives:expr,)* ] derive { $($body:tt)* }, $($rest:tt)*) => {
366 $crate::__custom_module_derives!(
367 @parse
368 [
369 $($derives,)*
370 $crate::__custom_module_derive_spec!(@unnamed [] [] $($body)*),
371 ]
372 $($rest)*
373 )
374 };
375 (@parse [ $($derives:expr,)* ] derive { $($body:tt)* }) => {
376 $crate::__custom_module_derives!(
377 @parse
378 [
379 $($derives,)*
380 $crate::__custom_module_derive_spec!(@unnamed [] [] $($body)*),
381 ]
382 )
383 };
384 (@parse [ $($derives:expr,)* ] derive $name:literal { $($body:tt)* }, $($rest:tt)*) => {
385 $crate::__custom_module_derives!(
386 @parse
387 [
388 $($derives,)*
389 $crate::__custom_module_derive_spec!(@named $name [] [] $($body)*),
390 ]
391 $($rest)*
392 )
393 };
394 (@parse [ $($derives:expr,)* ] derive $name:literal { $($body:tt)* }) => {
395 $crate::__custom_module_derives!(
396 @parse
397 [
398 $($derives,)*
399 $crate::__custom_module_derive_spec!(@named $name [] [] $($body)*),
400 ]
401 )
402 };
403}
404
405#[doc(hidden)]
406#[macro_export]
407macro_rules! __custom_module_derive_spec {
408 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ]) => {
409 concat!(
410 "{",
411 "\"functions\":[",
412 $crate::__custom_join!($($functions),*),
413 "],",
414 "\"modules\":[",
415 $crate::__custom_join!($($modules),*),
416 "]",
417 "}"
418 )
419 };
420 (@named_ident $name:ident [ $($functions:expr,)* ] [ $($modules:expr,)* ]) => {
421 concat!(
422 "{",
423 "\"name\":\"",
424 stringify!($name),
425 "\",",
426 "\"functions\":[",
427 $crate::__custom_join!($($functions),*),
428 "],",
429 "\"modules\":[",
430 $crate::__custom_join!($($modules),*),
431 "]",
432 "}"
433 )
434 };
435 (@named $name:literal [ $($functions:expr,)* ] [ $($modules:expr,)* ]) => {
436 concat!(
437 "{",
438 "\"name\":",
439 $crate::__custom_json_string_literal!($name),
440 ",",
441 "\"functions\":[",
442 $crate::__custom_join!($($functions),*),
443 "],",
444 "\"modules\":[",
445 $crate::__custom_join!($($modules),*),
446 "]",
447 "}"
448 )
449 };
450 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn $function:ident, $($rest:tt)*) => {
451 $crate::__custom_module_derive_spec!(
452 @unnamed
453 [ $($functions,)* concat!("\"", stringify!($function), "\""), ]
454 [ $($modules,)* ]
455 $($rest)*
456 )
457 };
458 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn $function:ident) => {
459 $crate::__custom_module_derive_spec!(
460 @unnamed
461 [ $($functions,)* concat!("\"", stringify!($function), "\""), ]
462 [ $($modules,)* ]
463 )
464 };
465 (@named $name:literal [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn $function:ident, $($rest:tt)*) => {
466 $crate::__custom_module_derive_spec!(
467 @named
468 $name
469 [ $($functions,)* concat!("\"", stringify!($function), "\""), ]
470 [ $($modules,)* ]
471 $($rest)*
472 )
473 };
474 (@named $name:literal [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn $function:ident) => {
475 $crate::__custom_module_derive_spec!(
476 @named
477 $name
478 [ $($functions,)* concat!("\"", stringify!($function), "\""), ]
479 [ $($modules,)* ]
480 )
481 };
482 (@named_ident $name:ident [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn $function:ident, $($rest:tt)*) => {
483 $crate::__custom_module_derive_spec!(
484 @named_ident
485 $name
486 [ $($functions,)* concat!("\"", stringify!($function), "\""), ]
487 [ $($modules,)* ]
488 $($rest)*
489 )
490 };
491 (@named_ident $name:ident [ $($functions:expr,)* ] [ $($modules:expr,)* ] fn $function:ident) => {
492 $crate::__custom_module_derive_spec!(
493 @named_ident
494 $name
495 [ $($functions,)* concat!("\"", stringify!($function), "\""), ]
496 [ $($modules,)* ]
497 )
498 };
499 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $name:literal { $($body:tt)* }, $($rest:tt)*) => {
500 $crate::__custom_module_derive_spec!(
501 @unnamed
502 [ $($functions,)* ]
503 [
504 $($modules,)*
505 $crate::__custom_module_derive_spec!(@named $name [] [] $($body)*),
506 ]
507 $($rest)*
508 )
509 };
510 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $name:literal { $($body:tt)* }) => {
511 $crate::__custom_module_derive_spec!(
512 @unnamed
513 [ $($functions,)* ]
514 [
515 $($modules,)*
516 $crate::__custom_module_derive_spec!(@named $name [] [] $($body)*),
517 ]
518 )
519 };
520 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $name:ident { $($body:tt)* }, $($rest:tt)*) => {
521 $crate::__custom_module_derive_spec!(
522 @unnamed
523 [ $($functions,)* ]
524 [
525 $($modules,)*
526 $crate::__custom_module_derive_spec!(@named_ident $name [] [] $($body)*),
527 ]
528 $($rest)*
529 )
530 };
531 (@unnamed [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $name:ident { $($body:tt)* }) => {
532 $crate::__custom_module_derive_spec!(
533 @unnamed
534 [ $($functions,)* ]
535 [
536 $($modules,)*
537 $crate::__custom_module_derive_spec!(@named_ident $name [] [] $($body)*),
538 ]
539 )
540 };
541 (@named $name:literal [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $module_name:ident { $($body:tt)* }, $($rest:tt)*) => {
542 $crate::__custom_module_derive_spec!(
543 @named
544 $name
545 [ $($functions,)* ]
546 [
547 $($modules,)*
548 $crate::__custom_module_derive_spec!(@named_ident $module_name [] [] $($body)*),
549 ]
550 $($rest)*
551 )
552 };
553 (@named $name:literal [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $module_name:ident { $($body:tt)* }) => {
554 $crate::__custom_module_derive_spec!(
555 @named
556 $name
557 [ $($functions,)* ]
558 [
559 $($modules,)*
560 $crate::__custom_module_derive_spec!(@named_ident $module_name [] [] $($body)*),
561 ]
562 )
563 };
564 (@named_ident $name:ident [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $module_name:ident { $($body:tt)* }, $($rest:tt)*) => {
565 $crate::__custom_module_derive_spec!(
566 @named_ident
567 $name
568 [ $($functions,)* ]
569 [
570 $($modules,)*
571 $crate::__custom_module_derive_spec!(@named_ident $module_name [] [] $($body)*),
572 ]
573 $($rest)*
574 )
575 };
576 (@named_ident $name:ident [ $($functions:expr,)* ] [ $($modules:expr,)* ] mod $module_name:ident { $($body:tt)* }) => {
577 $crate::__custom_module_derive_spec!(
578 @named_ident
579 $name
580 [ $($functions,)* ]
581 [
582 $($modules,)*
583 $crate::__custom_module_derive_spec!(@named_ident $module_name [] [] $($body)*),
584 ]
585 )
586 };
587}
588
589#[cfg(test)]
590mod tests {
591 use super::{ConfigSpec, FunctionSpec, ModuleDeriveSpec, ModuleSpec};
592 use crate::BuildConfig;
593
594 fn config_spec() -> ConfigSpec {
595 ConfigSpec {
596 schema_version: crate::SCHEMA_VERSION,
597 functions: vec![FunctionSpec {
598 pragma: "command".to_string(),
599 }],
600 modules: vec![ModuleSpec {
601 pragma: "tooling".to_string(),
602 derives: vec![ModuleDeriveSpec {
603 name: None,
604 functions: vec!["bootstrap".to_string(), "init".to_string()],
605 modules: vec![ModuleDeriveSpec {
606 name: Some("nested".to_string()),
607 functions: vec!["run".to_string()],
608 modules: vec![],
609 }],
610 }],
611 }],
612 }
613 }
614
615 const CONFIG_SPEC_USING_MACROS: &str = custom! {
616 fn pragma "command",
617 mod pragma "tooling" {
618 derive {
619 fn bootstrap,
620 fn init,
621 mod nested {
622 fn run,
623 }
624 }
625 }
626 };
627
628 const PHLOW_SPEC_MACROS: &str = custom! {
629 fn pragma "view",
630 mod pragma "extensions" {
631 derive {
632 mod __utilities {
633 fn phlow_to_string,
634 fn phlow_type_name,
635 fn phlow_create_view,
636 fn phlow_defining_methods,
637 }
638 }
639 }
640 };
641
642 fn phlow_spec() -> ConfigSpec {
643 ConfigSpec {
644 schema_version: crate::SCHEMA_VERSION,
645 functions: vec![FunctionSpec {
646 pragma: "view".to_string(),
647 }],
648 modules: vec![ModuleSpec {
649 pragma: "extensions".to_string(),
650 derives: vec![ModuleDeriveSpec {
651 name: Some("__utilities".to_string()),
652 functions: vec![
653 "phlow_to_string".to_string(),
654 "phlow_type_name".to_string(),
655 "phlow_create_view".to_string(),
656 "phlow_defining_methods".to_string(),
657 ],
658 modules: vec![],
659 }],
660 }],
661 }
662 }
663
664 const PHLOW_SPEC_JSON: &str = r#"
665 {
666 "schema_version": 1,
667 "functions": [ { "pragma": "view" } ],
668 "modules": [ {
669 "pragma": "extensions",
670 "derives": [
671 {
672 "name": "__utilities",
673 "functions": [
674 "phlow_to_string",
675 "phlow_type_name",
676 "phlow_create_view",
677 "phlow_defining_methods"
678 ]
679 }
680 ] } ]
681 }
682 "#;
683
684 #[test]
685 fn build_config_from_spec_collects_pragmas_and_module_derives() {
686 let spec = config_spec();
687 let config = BuildConfig::from_spec(&spec);
688
689 assert_eq!(config.pragmas, vec!["command", "tooling"]);
690 assert!(config.derives.is_empty());
691
692 let tooling_derives = config.module_derives.get("tooling").unwrap();
693 assert_eq!(tooling_derives.len(), 1);
694
695 let root_derive = &tooling_derives[0];
696 assert_eq!(root_derive.function_names(), ["bootstrap", "init"]);
697 assert_eq!(root_derive.modules().len(), 1);
698
699 let nested = &root_derive.modules()[0];
700 assert_eq!(nested.name(), Some("nested"));
701 assert_eq!(nested.function_names(), ["run"]);
702 }
703
704 #[test]
705 fn custom_macro_builds_the_same_config_spec() {
706 let spec = ConfigSpec::try_from(CONFIG_SPEC_USING_MACROS).unwrap();
707 assert_eq!(config_spec(), spec);
708 }
709
710 #[test]
711 fn phlow_macro_spec_matches_manual_spec() {
712 let spec = ConfigSpec::try_from(PHLOW_SPEC_MACROS).unwrap();
713 assert_eq!(phlow_spec(), spec);
714 }
715
716 #[test]
717 fn phlow_json_string_deserializes_as_manual_spec() {
718 let spec = ConfigSpec::try_from(PHLOW_SPEC_JSON).unwrap();
719 assert_eq!(phlow_spec(), spec);
720 }
721
722 #[test]
723 fn json_schema_version_must_match() {
724 let error = ConfigSpec::try_from(
725 r#"
726 {
727 "schema_version": 999,
728 "functions": []
729 }
730 "#,
731 )
732 .unwrap_err();
733
734 assert_eq!(
735 error.to_string(),
736 format!(
737 "schema_version 999 does not match supported version {}",
738 crate::SCHEMA_VERSION
739 )
740 );
741 }
742
743 #[test]
744 fn build_config_merge_combines_specs() {
745 const SECOND_SPEC: &str = custom! {
746 fn pragma "inspect",
747 mod pragma "extensions" {
748 derive {
749 mod diagnostics {
750 fn collect_warnings,
751 }
752 }
753 }
754 };
755
756 let first_spec = config_spec();
757 let second_spec = ConfigSpec::try_from(SECOND_SPEC).unwrap();
758 let mut config = BuildConfig::from_spec(&first_spec);
759 config.merge(BuildConfig::from_spec(&second_spec));
760
761 assert_eq!(
762 config.pragmas,
763 vec!["command", "tooling", "inspect", "extensions"]
764 );
765
766 let tooling_derives = config.module_derives.get("tooling").unwrap();
767 assert_eq!(tooling_derives.len(), 1);
768
769 let extension_derives = config.module_derives.get("extensions").unwrap();
770 assert_eq!(extension_derives.len(), 1);
771 assert_eq!(extension_derives[0].name(), Some("diagnostics"));
772 assert_eq!(extension_derives[0].function_names(), ["collect_warnings"]);
773 }
774
775 #[test]
776 fn macro_schema_version_matches_public_constant() {
777 assert_eq!(
778 crate::SCHEMA_VERSION.to_string(),
779 crate::__custom_schema_version!()
780 );
781 }
782}