1#[cfg(all(feature = "codespan-reporting", not(feature = "spanned")))]
25compile_error!("use of `codespan-reporting` feature is forbidden, use `spanned` INSTEAD");
26
27extern crate serde;
28#[macro_use]
29extern crate serde_derive;
30extern crate toml;
31extern crate unicode_segmentation;
32extern crate fmt2io;
33extern crate cargo_toml;
34#[cfg(feature = "man")]
35extern crate man;
36#[cfg(feature = "spanned")]
37extern crate codespan_reporting;
38
39pub(crate) mod config;
40pub(crate) mod codegen;
41#[cfg(feature = "man")]
42pub (crate) mod gen_man;
43#[cfg(feature = "debconf")]
44pub (crate) mod debconf;
45
46pub mod manifest;
47
48use std::borrow::Borrow;
49use std::fmt;
50use std::io::{self, Read, Write};
51use std::path::{Path, PathBuf};
52use manifest::LoadManifest;
53
54#[cfg(feature = "spanned")]
55type FileSpec = codespan_reporting::files::SimpleFile<String, String>;
56
57#[cfg(not(feature = "spanned"))]
58type FileSpec = ();
59
60#[derive(Debug)]
61enum ErrorData {
62 Input(InputError),
63 Io(io::Error),
64 Open { file: PathBuf, error: io::Error },
65 Manifest(manifest::Error),
66 MissingManifestDirEnvVar,
67 MissingOutDir,
68 #[cfg(feature = "debconf")]
69 Debconf(debconf::Error),
70}
71
72#[derive(Debug)]
73struct InputError {
74 #[cfg_attr(not(feature = "spanned"), allow(unused))]
75 file: FileSpec,
76 source: InputErrorSource,
77}
78
79#[derive(Debug)]
80enum InputErrorSource {
81 Toml(toml::de::Error),
82 Config(Vec<config::ValidationError>),
83}
84
85impl InputError {
86 #[cfg(feature = "spanned")]
87 fn to_diagnostics(&self) -> impl Iterator<Item=codespan_reporting::diagnostic::Diagnostic<()>> + '_ {
88 use codespan_reporting::diagnostic::Label;
89
90 match &self.source {
91 InputErrorSource::Toml(error) => {
92 let diagnostic = codespan_reporting::diagnostic::Diagnostic::error()
93 .with_message("failed to parse config specification");
94 let diagnostic = match error.line_col() {
95 Some((line, col)) => {
96 let line_sum = self.file.source().split('\n').take(line).map(|line| line.len()).sum::<usize>();
97 let start = line_sum + col + line;
100 let end = start + 1;
101 diagnostic.with_labels(vec![
102 Label::primary((), start..end).with_message(error.to_string()),
103 ])
104 },
105 None => diagnostic.with_notes(vec![error.to_string()]),
106 };
107 Some(diagnostic).into_iter().chain(None.into_iter().flatten())
108 },
109 InputErrorSource::Config(errors) => None.into_iter().chain(Some(errors.iter().map(|error| error.to_diagnostic(()))).into_iter().flatten()),
110 }
111 }
112}
113
114impl fmt::Display for InputError {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 match &self.source {
117 InputErrorSource::Toml(err) => write!(f, "failed to parse config specification: {}", err),
118 InputErrorSource::Config(errors) => {
119 for error in errors {
120 fmt::Display::fmt(&error, f)?;
121 writeln!(f)?;
122 }
123 Ok(())
124 },
125 }
126 }
127}
128
129
130impl From<toml::de::Error> for InputErrorSource {
131 fn from(value: toml::de::Error) -> Self {
132 InputErrorSource::Toml(value)
133 }
134}
135
136impl From<Vec<config::ValidationError>> for InputErrorSource {
137 fn from(value: Vec<config::ValidationError>) -> Self {
138 InputErrorSource::Config(value)
139 }
140}
141
142pub struct Error {
144 data: ErrorData,
145}
146
147impl fmt::Display for Error {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 match &self.data {
150 ErrorData::Manifest(error) => write!(f, "failed to process manifest: {}", error),
151 ErrorData::Input(error) => fmt::Display::fmt(error, f),
152 ErrorData::Io(err) => write!(f, "I/O error: {}", err),
153 ErrorData::Open { file, error } => write!(f, "failed to open file {}: {}", file.display(), error),
154 ErrorData::MissingManifestDirEnvVar => write!(f, "missing environment variable: CARGO_MANIFEST_DIR"),
155 ErrorData::MissingOutDir => write!(f, "missing environment variable: OUT_DIR"),
156 #[cfg(feature = "debconf")]
157 ErrorData::Debconf(err) => write!(f, "failed to generate debconf: {}", err),
158 }
159 }
160}
161
162impl fmt::Debug for Error {
164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 #[cfg(feature = "spanned")]
166 {
167 use codespan_reporting::term::termcolor::{NoColor};
168
169 struct WrapIo<W: fmt::Write>(W);
170
171 impl<W: fmt::Write> std::io::Write for WrapIo<W> {
172 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
173 self.0.write_str(std::str::from_utf8(buf).unwrap()).map_err(|_| std::io::ErrorKind::Other)?;
174 Ok(buf.len())
175 }
176
177 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
178 self.0.write_str(std::str::from_utf8(buf).unwrap()).map_err(|_| std::io::ErrorKind::Other.into())
179 }
180
181 fn flush(&mut self) -> std::io::Result<()> {
182 Ok(())
183 }
184 }
185
186 if let ErrorData::Input(error) = &self.data {
187 writeln!(f, "invalid config specification:")?;
188 let diagnostics = error.to_diagnostics();
189
190 let mut writer = NoColor::new(WrapIo(&mut *f));
191 let config = codespan_reporting::term::Config::default();
192
193 for diagnostic in diagnostics {
194 match codespan_reporting::term::emit(&mut writer, &config, &error.file, &diagnostic) {
195 Ok(()) => (),
196 Err(codespan_reporting::files::Error::Io(_)) => return Err(fmt::Error),
197 Err(other) => panic!("unexpected error: {}", other),
198 }
199 }
200 return Ok(());
201 }
202 }
203 fmt::Display::fmt(self, f)
204 }
205}
206
207#[cfg(not(feature = "spanned"))]
208impl Error {
209 pub fn report(&self) -> std::io::Result<()> {
224 write!(std::io::stderr().lock(), "{}", self)
225 }
226}
227
228#[cfg(feature = "spanned")]
229impl Error {
230 pub fn report(&self) -> std::io::Result<()> {
245 use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
246
247 if let ErrorData::Input(error) = &self.data {
248 let diagnostics = error.to_diagnostics();
249
250 let writer = StandardStream::stderr(ColorChoice::Always);
251 let mut writer = writer.lock();
252 let config = codespan_reporting::term::Config::default();
253
254 for diagnostic in diagnostics {
255 match codespan_reporting::term::emit(&mut writer, &config, &error.file, &diagnostic) {
256 Ok(()) => (),
257 Err(codespan_reporting::files::Error::Io(error)) => return Err(error),
258 Err(other) => panic!("unexpected error: {}", other),
259 }
260 }
261 Ok(())
262 } else {
263 write!(std::io::stderr().lock(), "{}", self)
264 }
265 }
266}
267
268impl Error {
269 pub fn report_and_exit(&self) -> ! {
275 self.report().expect("failed to write to stderr");
276 std::process::exit(1);
277 }
278}
279
280impl From<ErrorData> for Error {
281 fn from(data: ErrorData) -> Self {
282 Error {
283 data,
284 }
285 }
286}
287
288impl From<io::Error> for Error {
289 fn from(err: io::Error) -> Self {
290 Error {
291 data: ErrorData::Io(err),
292 }
293 }
294}
295
296impl From<manifest::Error> for Error {
297 fn from(err: manifest::Error) -> Self {
298 Error {
299 data: ErrorData::Manifest(err),
300 }
301 }
302}
303
304impl From<manifest::LoadError> for Error {
305 fn from(err: manifest::LoadError) -> Self {
306 Error {
307 data: ErrorData::Manifest(err.into()),
308 }
309 }
310}
311
312impl From<void::Void> for Error {
313 fn from(value: void::Void) -> Self {
314 match value {}
315 }
316}
317
318
319#[cfg(feature = "debconf")]
320impl From<debconf::Error> for Error {
321 fn from(err: debconf::Error) -> Self {
322 Error {
323 data: ErrorData::Debconf(err),
324 }
325 }
326}
327
328fn load<S: Read, N: fmt::Display>(mut source: S, name: N) -> Result<config::Config, Error> {
329 let mut data = String::new();
330 source.read_to_string(&mut data)?;
331 (|| {
332 let cfg = toml::from_str::<config::raw::Config>(&data)?;
333 let cfg = cfg.validate()?;
334
335 Ok(cfg)
336 })().map_err(|source| {
337 #[cfg(feature = "spanned")]
338 {
339 ErrorData::Input(InputError { file: FileSpec::new(name.to_string(), data), source }).into()
340 }
341 #[cfg(not(feature = "spanned"))]
342 {
343 let _ = name;
344 ErrorData::Input(InputError{ file: (), source }).into()
345 }
346 })
347}
348
349fn load_from_file<P: AsRef<Path>>(source: P) -> Result<::config::Config, Error> {
350 let config_spec = std::fs::File::open(&source).map_err(|error| ErrorData::Open { file: source.as_ref().into(), error })?;
351
352 load(config_spec, source.as_ref().display())
353}
354
355fn path_in_out_dir<P: AsRef<Path>>(file_name: P) -> Result<PathBuf, Error> {
356 let mut out: PathBuf = std::env::var_os("OUT_DIR").ok_or(ErrorData::MissingOutDir)?.into();
357 out.push(file_name);
358
359 Ok(out)
360}
361
362fn default_out_file(binary: Option<&str>) -> Result<PathBuf, Error> {
363 const GENERATED_FILE_NAME: &str = "configure_me_config.rs";
364
365 let file_name_owned;
366 let file_name = match binary {
367 Some(binary) => {
368 file_name_owned = format!("{}_{}", binary, GENERATED_FILE_NAME);
369 &file_name_owned
370 },
371 None => GENERATED_FILE_NAME,
372 };
373 path_in_out_dir(file_name)
374}
375
376fn create_file<P: AsRef<Path> + Into<PathBuf>>(file: P) -> Result<std::fs::File, Error> {
378 std::fs::File::create(&file)
379 .map_err(|error| ErrorData::Open { file: file.into(), error })
380 .map_err(Into::into)
381}
382
383fn generate_to_file<P: AsRef<Path> + Into<PathBuf>>(config_spec: &::config::Config, file: P) -> Result<(), Error> {
384 let config_code = create_file(file)?;
385 ::fmt2io::write(config_code, |config_code| codegen::generate_code(config_spec, config_code)).map_err(Into::into)
386}
387
388fn load_and_generate_default<P: AsRef<Path>>(source: P, binary: Option<&str>) -> Result<::config::Config, Error> {
389 let config_spec = load_from_file(&source)?;
390 generate_to_file(&config_spec, default_out_file(binary)?)?;
391 #[cfg(feature = "debconf")]
392 debconf::generate_if_requested(&config_spec)?;
393 println!("cargo:rerun-if-changed={}", source.as_ref().display());
394 Ok(config_spec)
395}
396
397pub fn generate_source<S: Read, O: Write>(source: S, output: O) -> Result<(), Error> {
399 let cfg = load(source, "unknown file")?;
400
401 ::fmt2io::write(output, |output| codegen::generate_code(&cfg, output)).map_err(Into::into)
402}
403
404#[deprecated = "Use build_script_auto and put the path into Cargo.toml to expose it to external tools"]
414pub fn build_script<P: AsRef<Path>>(source: P) -> Result<(), Error> {
415 load_and_generate_default(source, None).map(::std::mem::drop)
416}
417
418pub fn build_script_auto() -> Result<(), Error> {
426 use manifest::SpecificationPaths;
427
428 let manifest_dir = manifest::get_dir()?;
429 let manifest_file = manifest_dir.join("Cargo.toml");
430
431 let paths = manifest_file
432 .load_manifest()?
433 .package.ok_or(manifest::Error::MissingPackage)?
434 .metadata.ok_or(manifest::Error::MissingMetadata)?
435 .configure_me.ok_or(manifest::Error::MissingConfigureMeMetadata)?
436 .spec_paths;
437
438 match paths {
439 SpecificationPaths::Single(path) => load_and_generate_default(manifest_dir.join(path), None).map(::std::mem::drop),
440 SpecificationPaths::PerBinary(binaries) => {
441 for (binary, path) in binaries {
442 load_and_generate_default(manifest_dir.join(path), Some(&binary)).map(::std::mem::drop)?;
443 }
444 Ok(())
445 },
446 SpecificationPaths::Other(other) => match other._private {},
447 }
448}
449
450#[cfg(feature = "unstable-metabuild")]
451pub fn metabuild() {
452 build_script_auto().unwrap_or_else(|error| {
453 println!("Could not generate configuration parser: {}", error);
454 std::process::exit(1)
455 })
456}
457
458#[cfg(feature = "man")]
468#[deprecated = "use of cfg_me crate to build man pages is cleaner"]
469pub fn build_script_with_man<P: AsRef<Path>>(source: P) -> Result<(), Error> {
470 #[allow(deprecated)]
471 build_script_with_man_written_to(source, path_in_out_dir("app.man")?)
472}
473
474#[cfg(feature = "man")]
484#[deprecated = "use of cfg_me crate to build man pages is cleaner"]
485pub fn build_script_with_man_written_to<P: AsRef<Path>, M: AsRef<Path> + Into<PathBuf>>(source: P, output: M) -> Result<(), Error> {
486 let config_spec = load_and_generate_default(source, None)?;
487 let manifest = manifest::BuildScript.load_manifest()?;
488 let man_page = gen_man::generate_man_page(&config_spec, manifest.borrow())?;
489
490 let mut file = create_file(output)?;
491 file.write_all(man_page.as_bytes())?;
492 #[cfg(feature = "debconf")]
493 debconf::generate_if_requested(&config_spec)?;
494 Ok(())
495}
496
497#[cfg(feature = "man")]
501pub fn generate_man<M: LoadManifest, W: std::io::Write, S: AsRef<Path>>(source: S, mut dest: W, manifest: M) -> Result<(), Error> where Error: std::convert::From<<M as manifest::LoadManifest>::Error> {
502 let config_spec = load_from_file(&source)?;
503 let manifest = manifest.load_manifest()?;
504 let man_page = gen_man::generate_man_page(&config_spec, manifest.borrow())?;
505 dest.write_all(man_page.as_bytes())?;
506 Ok(())
507}
508
509#[cfg(test)]
510#[deny(warnings)]
511pub(crate) mod tests {
512 use ::generate_source;
513
514 pub const SINGLE_OPTIONAL_PARAM: &str =
515r#"
516[general]
517env_prefix = "TEST_APP"
518
519[[param]]
520name = "foo"
521type = "u32"
522"#;
523
524 pub const SINGLE_MANDATORY_PARAM: &str =
525r#"
526[general]
527env_prefix = "TEST_APP"
528
529[[param]]
530name = "foo"
531type = "u32"
532optional = false
533"#;
534
535 pub const SINGLE_DEFAULT_PARAM: &str =
536r#"
537[general]
538env_prefix = "TEST_APP"
539
540[[param]]
541name = "foo"
542type = "u32"
543default = "42"
544"#;
545
546 pub const SINGLE_SWITCH: &str =
547r#"
548[general]
549env_prefix = "TEST_APP"
550
551[[switch]]
552name = "foo"
553"#;
554
555 pub const MULTIPLE_PARAMS: &str =
556r#"
557[general]
558env_prefix = "TEST_APP"
559
560[[param]]
561name = "foo"
562type = "u32"
563default = "42"
564doc = "A foo"
565
566[[param]]
567name = "bar"
568type = "String"
569optional = true
570doc = "A very, very, very, very, very, very, very, very, very, very, very, very, very, very long documentation..."
571
572[[param]]
573name = "baz"
574type = "String"
575optional = false
576doc = "A much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much, much longer documentation..."
577
578[[switch]]
579name = "verbose"
580# doc intentionally missing, because it's obious...
581
582[[switch]]
583name = "fast"
584default = true
585doc = "Determines whether to mine bitcoins fast or slowly"
586"#;
587
588 pub const NO_ARG: &str =
589r#"
590[general]
591env_prefix = "TEST_APP"
592
593[[param]]
594name = "foo"
595type = "u32"
596argument = false
597"#;
598
599 pub const SHORT_SWITCHES: &str =
600r#"
601[[switch]]
602name = "a"
603abbr = "a"
604doc = "test"
605
606[[switch]]
607name = "b"
608abbr = "b"
609
610[[switch]]
611name = "c"
612abbr = "c"
613count = true
614
615[[param]]
616name = "d"
617type = "String"
618abbr = "d"
619optional = true
620
621[[param]]
622name = "e"
623type = "String"
624abbr = "e"
625optional = true
626
627[[switch]]
628name = "foo_bar"
629abbr = "f"
630"#;
631
632 pub const CONF_FILES: &str =
633r#"
634[general]
635env_prefix = "TEST_APP"
636conf_file_param = "config"
637conf_dir_param = "conf_dir"
638
639[[param]]
640name = "foo"
641type = "u32"
642doc = "A foo"
643"#;
644
645 pub const CUSTOM_MERGE_FN: &str =
646r#"
647[general]
648env_prefix = "TEST_APP"
649
650[[param]]
651name = "foo"
652type = "u32"
653merge_fn = "(|a: &mut u32, b: u32| *a += b)"
654
655[[param]]
656name = "bar"
657type = "String"
658merge_fn = "(|a: &mut String, b: String| a.push_str(&b))"
659"#;
660
661 #[allow(unused)]
662 pub struct ExpectedOutput {
663 pub raw_config: &'static str,
664 pub validate: &'static str,
665 pub merge_in: &'static str,
666 pub merge_args: &'static str,
667 pub config: &'static str,
668 pub arg_parse_error: &'static str,
669 }
670
671 pub const EXPECTED_EMPTY: ExpectedOutput = ExpectedOutput {
672 raw_config: include_str!("../tests/expected_outputs/empty/raw_config.rs"),
673 validate: include_str!("../tests/expected_outputs/empty/validate.rs"),
674 merge_in: include_str!("../tests/expected_outputs/empty/merge_in.rs"),
675 merge_args: include_str!("../tests/expected_outputs/empty/merge_args.rs"),
676 config: include_str!("../tests/expected_outputs/empty/config.rs"),
677 arg_parse_error: include_str!("../tests/expected_outputs/empty/arg_parse_error.rs"),
678 };
679
680 pub const EXPECTED_SINGLE_OPTIONAL_PARAM: ExpectedOutput = ExpectedOutput {
681 raw_config: include_str!("../tests/expected_outputs/single_optional_param/raw_config.rs"),
682 validate: include_str!("../tests/expected_outputs/single_optional_param/validate.rs"),
683 merge_in: include_str!("../tests/expected_outputs/single_optional_param/merge_in.rs"),
684 merge_args: include_str!("../tests/expected_outputs/single_optional_param/merge_args.rs"),
685 config: include_str!("../tests/expected_outputs/single_optional_param/config.rs"),
686 arg_parse_error: include_str!("../tests/expected_outputs/single_optional_param/arg_parse_error.rs"),
687 };
688
689 pub const EXPECTED_SINGLE_MANDATORY_PARAM: ExpectedOutput = ExpectedOutput {
690 raw_config: include_str!("../tests/expected_outputs/single_mandatory_param/raw_config.rs"),
691 validate: include_str!("../tests/expected_outputs/single_mandatory_param/validate.rs"),
692 merge_in: include_str!("../tests/expected_outputs/single_mandatory_param/merge_in.rs"),
693 merge_args: include_str!("../tests/expected_outputs/single_mandatory_param/merge_args.rs"),
694 config: include_str!("../tests/expected_outputs/single_mandatory_param/config.rs"),
695 arg_parse_error: include_str!("../tests/expected_outputs/single_mandatory_param/arg_parse_error.rs"),
696 };
697
698 pub const EXPECTED_SINGLE_DEFAULT_PARAM: ExpectedOutput = ExpectedOutput {
699 raw_config: include_str!("../tests/expected_outputs/single_default_param/raw_config.rs"),
700 validate: include_str!("../tests/expected_outputs/single_default_param/validate.rs"),
701 merge_in: include_str!("../tests/expected_outputs/single_default_param/merge_in.rs"),
702 merge_args: include_str!("../tests/expected_outputs/single_default_param/merge_args.rs"),
703 config: include_str!("../tests/expected_outputs/single_default_param/config.rs"),
704 arg_parse_error: include_str!("../tests/expected_outputs/single_default_param/arg_parse_error.rs"),
705 };
706
707 pub const EXPECTED_SINGLE_SWITCH: ExpectedOutput = ExpectedOutput {
708 raw_config: include_str!("../tests/expected_outputs/single_switch/raw_config.rs"),
709 validate: include_str!("../tests/expected_outputs/single_switch/validate.rs"),
710 merge_in: include_str!("../tests/expected_outputs/single_switch/merge_in.rs"),
711 merge_args: include_str!("../tests/expected_outputs/single_switch/merge_args.rs"),
712 config: include_str!("../tests/expected_outputs/single_switch/config.rs"),
713 arg_parse_error: include_str!("../tests/expected_outputs/single_switch/arg_parse_error.rs"),
714 };
715
716 pub const EXPECTED_SHORT_SWITCHES: ExpectedOutput = ExpectedOutput {
717 raw_config: include_str!("../tests/expected_outputs/short_switches/raw_config.rs"),
718 validate: include_str!("../tests/expected_outputs/short_switches/validate.rs"),
719 merge_in: include_str!("../tests/expected_outputs/short_switches/merge_in.rs"),
720 merge_args: include_str!("../tests/expected_outputs/short_switches/merge_args.rs"),
721 config: include_str!("../tests/expected_outputs/short_switches/config.rs"),
722 arg_parse_error: include_str!("../tests/expected_outputs/short_switches/arg_parse_error.rs"),
723 };
724
725 fn check(src: &str, expected: &str) {
726 use std::io::Write;
727
728 let mut src = src.as_bytes();
729 let mut out = Vec::new();
730 generate_source(&mut src, &mut out).unwrap();
731 if out != expected.as_bytes() {
732 let mut expected_temp = tempfile::Builder::new().prefix("expected").tempfile().unwrap();
733 let mut out_temp = tempfile::Builder::new().prefix("output").tempfile().unwrap();
734
735 expected_temp.write_all(expected.as_bytes()).unwrap();
736 out_temp.write_all(&out).unwrap();
737
738 std::process::Command::new("diff")
739 .arg(expected_temp.path())
740 .arg(out_temp.path())
741 .spawn()
742 .expect("failed to run diff")
743 .wait()
744 .unwrap();
745 panic!("output differs from expected");
746 }
747 }
748
749 #[test]
750 fn empty() {
751 check("", include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/empty-config.rs")));
752 }
753
754 #[test]
755 fn single_optional_param() {
756 check(SINGLE_OPTIONAL_PARAM, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/single_optional_param-config.rs")));
757 }
758
759 #[test]
760 fn single_mandatory_param() {
761 check(SINGLE_MANDATORY_PARAM, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/single_mandatory_param-config.rs")));
762 }
763
764 #[test]
765 fn single_default_param() {
766 check(SINGLE_DEFAULT_PARAM, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/single_default_param-config.rs")));
767 }
768
769 #[test]
770 fn single_switch() {
771 check(SINGLE_SWITCH, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/single_switch-config.rs")));
772 }
773
774 #[test]
775 fn multiple_params() {
776 check(MULTIPLE_PARAMS, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/multiple_params-config.rs")));
777 }
778
779 #[test]
780 fn no_arg() {
781 check(NO_ARG, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/no_arg-config.rs")));
782 }
783
784 #[test]
785 fn short_switches() {
786 check(SHORT_SWITCHES, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/short_switches-config.rs")));
787 }
788
789 #[test]
790 fn conf_files() {
791 check(CONF_FILES, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/conf_files-config.rs")));
792 }
793
794 #[test]
795 fn custom_merge_fn() {
796 check(CUSTOM_MERGE_FN, include_str!(concat!(env!("OUT_DIR"), "/expected_outputs/with_custom_merge-config.rs")));
797 }
798}