1#![cfg_attr(docsrs, feature(doc_cfg))]
2use serde::{de::DeserializeOwned, Serialize};
103#[allow(unused_imports)]
104use serde_path_to_error::{deserialize as depath, serialize as serpath, Error as PathError};
105use std::fs::File;
106use std::io::{self, Write};
107use std::path::Path;
108use strum::{Display, EnumIter, EnumString};
109use thiserror::Error;
110
111#[cfg(feature = "ron")]
112use ron::ser::PrettyConfig;
113
114#[derive(
123 Clone, Copy, Debug, Display, EnumIter, EnumString, Eq, Hash, Ord, PartialEq, PartialOrd,
124)]
125#[strum(ascii_case_insensitive, serialize_all = "UPPERCASE")]
126#[non_exhaustive]
127pub enum Format {
128 #[cfg(feature = "json")]
133 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
134 Json,
135
136 #[cfg(feature = "json5")]
143 #[cfg_attr(docsrs, doc(cfg(feature = "json5")))]
144 Json5,
145
146 #[cfg(feature = "ron")]
151 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
152 Ron,
153
154 #[cfg(feature = "toml")]
160 #[cfg_attr(docsrs, doc(cfg(feature = "toml")))]
161 Toml,
162
163 #[cfg(feature = "yaml")]
166 #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))]
167 Yaml,
168}
169
170impl Format {
171 pub fn iter() -> FormatIter {
173 <Format as strum::IntoEnumIterator>::iter()
175 }
176
177 #[cfg_attr(all(feature = "json", feature = "yaml"), doc = concat!(
183 "# Example\n",
184 "\n",
185 "```\n",
186 "use cfgfifo::Format;\n",
187 "\n",
188 "assert_eq!(Format::Json.extensions(), &[\"json\"]);\n",
189 "assert_eq!(Format::Yaml.extensions(), &[\"yaml\", \"yml\"]);\n",
190 "```\n",
191 ))]
192 pub fn extensions(&self) -> &'static [&'static str] {
193 match self {
194 #[cfg(feature = "json")]
195 Format::Json => &["json"],
196 #[cfg(feature = "json5")]
197 Format::Json5 => &["json5"],
198 #[cfg(feature = "ron")]
199 Format::Ron => &["ron"],
200 #[cfg(feature = "toml")]
201 Format::Toml => &["toml"],
202 #[cfg(feature = "yaml")]
203 Format::Yaml => &["yaml", "yml"],
204 #[allow(unreachable_patterns)]
205 _ => unreachable!(),
206 }
207 }
208
209 #[cfg_attr(feature = "json", doc = concat!(
214 "# Example\n",
215 "\n",
216 "```\n",
217 "use cfgfifo::Format;\n",
218 "\n",
219 "assert!(Format::Json.has_extension(\".json\"));\n",
220 "assert!(Format::Json.has_extension(\"JSON\"));\n",
221 "assert!(!Format::Json.has_extension(\"cfg\"));\n",
222 "```\n",
223 ))]
224 pub fn has_extension(&self, ext: &str) -> bool {
225 let ext = ext.strip_prefix('.').unwrap_or(ext);
226 self.extensions()
227 .iter()
228 .any(|x| x.eq_ignore_ascii_case(ext))
229 }
230
231 #[cfg_attr(all(feature = "json", feature = "yaml"), doc = concat!(
237 "# Example\n",
238 "\n",
239 "```\n",
240 "use cfgfifo::Format;\n",
241 "\n",
242 "assert_eq!(Format::from_extension(\".json\"), Some(Format::Json));\n",
243 "assert_eq!(Format::from_extension(\"YML\"), Some(Format::Yaml));\n",
244 "assert_eq!(Format::from_extension(\"cfg\"), None);\n",
245 "```\n",
246 ))]
247 pub fn from_extension(ext: &str) -> Option<Format> {
248 Format::iter().find(|f| f.has_extension(ext))
249 }
250
251 #[cfg_attr(all(feature = "json", feature = "ron"), doc = concat!(
253 "# Example\n",
254 "\n",
255 "```\n",
256 "use cfgfifo::Format;\n",
257 "\n",
258 "assert_eq!(Format::identify(\"path/to/file.json\").unwrap(), Format::Json);\n",
259 "assert_eq!(Format::identify(\"path/to/file.RON\").unwrap(), Format::Ron);\n",
260 "assert!(Format::identify(\"path/to/file.cfg\").is_err());\n",
261 "assert!(Format::identify(\"path/to/file\").is_err());\n",
262 "```\n",
263 ))]
264 pub fn identify<P: AsRef<Path>>(path: P) -> Result<Format, IdentifyError> {
270 let ext = get_ext(path.as_ref())?;
271 Format::from_extension(ext).ok_or_else(|| IdentifyError::Unknown(ext.to_owned()))
272 }
273
274 #[cfg_attr(feature = "json", doc = concat!(
276 "# Example\n",
277 "\n",
278 "```\n",
279 "use cfgfifo::Format;\n",
280 "use serde::Serialize;\n",
281 "\n",
282 "#[derive(Clone, Debug, Eq, PartialEq, Serialize)]\n",
283 "struct Data {\n",
284 " name: String,\n",
285 " size: u32,\n",
286 " enabled: bool,\n",
287 "}\n",
288 "\n",
289 "let datum = Data {\n",
290 " name: String::from(\"Example\"),\n",
291 " size: 42,\n",
292 " enabled: true,\n",
293 "};\n",
294 "\n",
295 "let s = Format::Json.dump_to_string(&datum).unwrap();\n",
296 "\n",
297 "assert_eq!(\n",
298 " s,\n",
299 " concat!(\n",
300 " \"{\\n\",\n",
301 " \" \\\"name\\\": \\\"Example\\\",\\n\",\n",
302 " \" \\\"size\\\": 42,\\n\",\n",
303 " \" \\\"enabled\\\": true\\n\",\n",
304 " \"}\"\n",
305 " )\n",
306 ");\n",
307 "```\n",
308 ))]
309 #[allow(unused_variables)]
313 pub fn dump_to_string<T: Serialize>(&self, value: &T) -> Result<String, SerializeError> {
314 match self {
315 #[cfg(feature = "json")]
316 Format::Json => {
317 let mut buffer = Vec::new();
318 let mut ser = serde_json::Serializer::pretty(&mut buffer);
319 serpath(value, &mut ser)?;
320 let Ok(s) = String::from_utf8(buffer) else {
321 unreachable!("serialized JSON should be valid UTF-8");
322 };
323 Ok(s)
324 }
325 #[cfg(feature = "json5")]
326 Format::Json5 => {
327 let mut buffer = Vec::new();
329 let mut ser = serde_json::Serializer::pretty(&mut buffer);
330 serpath(value, &mut ser)?;
331 let Ok(s) = String::from_utf8(buffer) else {
332 unreachable!("serialized JSON should be valid UTF-8");
333 };
334 Ok(s)
335 }
336 #[cfg(feature = "ron")]
337 Format::Ron => {
338 let mut buffer = Vec::new();
339 let mut ser = ron::Serializer::new(&mut buffer, Some(ron_config()))
340 .map_err(SerializeError::RonStart)?;
341 serpath(value, &mut ser)?;
342 let Ok(s) = String::from_utf8(buffer) else {
343 unreachable!("serialized RON should be valid UTF-8");
344 };
345 Ok(s)
346 }
347 #[cfg(feature = "toml")]
348 Format::Toml => {
349 let mut s = String::new();
350 let ser = toml::Serializer::pretty(&mut s);
351 serpath(value, ser)?;
352 Ok(s)
353 }
354 #[cfg(feature = "yaml")]
355 Format::Yaml => {
356 let mut buffer = Vec::new();
357 self.dump_to_writer(&mut buffer, value)?;
358 let Ok(s) = String::from_utf8(buffer) else {
359 unreachable!("serialized YAML should be valid UTF-8");
360 };
361 Ok(s)
362 }
363 #[allow(unreachable_patterns)]
364 _ => unreachable!(),
365 }
366 }
367
368 #[cfg_attr(feature = "yaml", doc = concat!(
370 "# Example\n",
371 "\n",
372 "```\n",
373 "use cfgfifo::Format;\n",
374 "use serde::Deserialize;\n",
375 "\n",
376 "#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]\n",
377 "struct Data {\n",
378 " name: String,\n",
379 " size: u32,\n",
380 " enabled: bool,\n",
381 "}\n",
382 "\n",
383 "let s = concat!(\n",
384 " \"name: Example\\n\",\n",
385 " \"size: 42\\n\",\n",
386 " \"enabled: true\\n\",\n",
387 ");\n",
388 "\n",
389 "let datum: Data = Format::Yaml.load_from_str(s).unwrap();\n",
390 "\n",
391 "assert_eq!(\n",
392 " datum,\n",
393 " Data {\n",
394 " name: String::from(\"Example\"),\n",
395 " size: 42,\n",
396 " enabled: true,\n",
397 " }\n",
398 ");\n",
399 "```\n",
400 ))]
401 #[allow(unused_variables)]
405 pub fn load_from_str<T: DeserializeOwned>(&self, s: &str) -> Result<T, DeserializeError> {
406 match self {
407 #[cfg(feature = "json")]
408 Format::Json => {
409 let mut de = serde_json::Deserializer::from_str(s);
410 let value = depath(&mut de)?;
411 de.end().map_err(DeserializeError::JsonEnd)?;
412 Ok(value)
413 }
414 #[cfg(feature = "json5")]
415 Format::Json5 => {
416 let mut de =
417 json5::Deserializer::from_str(s).map_err(DeserializeError::Json5Syntax)?;
418 depath(&mut de).map_err(Into::into)
419 }
420 #[cfg(feature = "ron")]
421 Format::Ron => {
422 let mut de = ron::Deserializer::from_str(s).map_err(DeserializeError::RonStart)?;
423 let value = match depath(&mut de) {
424 Ok(value) => value,
425 Err(e) => {
426 let path = e.path().clone();
427 let inner = e.into_inner();
428 let ron_e = de.span_error(inner);
429 return Err(DeserializeError::Ron(PathError::new(path, ron_e)));
430 }
431 };
432 de.end()
433 .map_err(|e| DeserializeError::RonEnd(de.span_error(e)))?;
434 Ok(value)
435 }
436 #[cfg(feature = "toml")]
437 Format::Toml => {
438 let de = toml::Deserializer::new(s);
439 depath(de).map_err(Into::into)
440 }
441 #[cfg(feature = "yaml")]
442 Format::Yaml => {
443 let de = serde_yaml::Deserializer::from_str(s);
444 depath(de).map_err(Into::into)
445 }
446 #[allow(unreachable_patterns)]
447 _ => unreachable!(),
448 }
449 }
450
451 #[allow(unused_mut, unused_variables)]
462 pub fn dump_to_writer<W: Write, T: Serialize>(
463 &self,
464 mut writer: W,
465 value: &T,
466 ) -> Result<(), SerializeError> {
467 match self {
468 #[cfg(feature = "json")]
469 Format::Json => {
470 let mut ser = serde_json::Serializer::pretty(&mut writer);
471 serpath(value, &mut ser)?;
472 writer.write_all(b"\n")?;
473 Ok(())
474 }
475 #[cfg(feature = "json5")]
476 Format::Json5 => {
477 let mut ser = serde_json::Serializer::pretty(&mut writer);
480 serpath(value, &mut ser)?;
481 writer.write_all(b"\n")?;
482 Ok(())
483 }
484 #[cfg(feature = "ron")]
485 Format::Ron => {
486 let mut ser = ron::Serializer::new(&mut writer, Some(ron_config()))
487 .map_err(SerializeError::RonStart)?;
488 serpath(value, &mut ser)?;
489 writer.write_all(b"\n")?;
490 Ok(())
491 }
492 #[cfg(feature = "toml")]
493 Format::Toml => {
494 let s = self.dump_to_string(value)?;
495 writer.write_all(s.as_bytes())?;
496 Ok(())
497 }
498 #[cfg(feature = "yaml")]
499 Format::Yaml => {
500 let mut ser = serde_yaml::Serializer::new(writer);
501 serpath(value, &mut ser).map_err(Into::into)
502 }
503 #[allow(unreachable_patterns)]
504 _ => unreachable!(),
505 }
506 }
507
508 #[allow(unused_variables)]
515 pub fn load_from_reader<R: io::Read, T: DeserializeOwned>(
516 &self,
517 reader: R,
518 ) -> Result<T, DeserializeError> {
519 match self {
520 #[cfg(feature = "json")]
521 Format::Json => {
522 let mut de = serde_json::Deserializer::from_reader(reader);
523 let value = depath(&mut de)?;
524 de.end().map_err(DeserializeError::JsonEnd)?;
525 Ok(value)
526 }
527 #[cfg(feature = "json5")]
528 Format::Json5 => {
529 let s = io::read_to_string(reader)?;
530 self.load_from_str(&s)
531 }
532 #[cfg(feature = "ron")]
533 Format::Ron => {
534 let s = io::read_to_string(reader)?;
535 self.load_from_str(&s)
536 }
537 #[cfg(feature = "toml")]
538 Format::Toml => {
539 let s = io::read_to_string(reader)?;
540 self.load_from_str(&s)
541 }
542 #[cfg(feature = "yaml")]
543 Format::Yaml => {
544 let de = serde_yaml::Deserializer::from_reader(reader);
545 depath(de).map_err(Into::into)
546 }
547 #[allow(unreachable_patterns)]
548 _ => unreachable!(),
549 }
550 }
551}
552
553pub fn load<T: DeserializeOwned, P: AsRef<Path>>(path: P) -> Result<T, LoadError> {
562 Cfgfifo::default().load(path)
563}
564
565pub fn dump<P: AsRef<Path>, T: Serialize>(path: P, value: &T) -> Result<(), DumpError> {
574 Cfgfifo::default().dump(path, value)
575}
576
577#[derive(Clone, Debug, Eq, PartialEq)]
586pub struct Cfgfifo {
587 formats: Vec<Format>,
588 fallback: Option<Format>,
589}
590
591impl Cfgfifo {
592 pub fn new() -> Cfgfifo {
594 Cfgfifo {
595 formats: Format::iter().collect(),
596 fallback: None,
597 }
598 }
599
600 pub fn formats<I: IntoIterator<Item = Format>>(mut self, iter: I) -> Self {
610 self.formats = iter.into_iter().collect();
611 self
612 }
613
614 pub fn fallback(mut self, fallback: Option<Format>) -> Self {
616 self.fallback = fallback;
617 self
618 }
619
620 #[cfg_attr(all(feature = "json", feature = "yaml"), doc = concat!(
622 "# Example\n",
623 "\n",
624 "```\n",
625 "use cfgfifo::{Cfgfifo, Format};\n",
626 "\n",
627 "let cfgfifo = Cfgfifo::new()\n",
628 " .formats([Format::Json, Format::Yaml])\n",
629 " .fallback(Some(Format::Json));\n",
630 "\n",
631 "assert_eq!(cfgfifo.identify(\"path/to/file.json\").unwrap(), Format::Json);\n",
632 "assert_eq!(cfgfifo.identify(\"path/to/file.YML\").unwrap(), Format::Yaml);\n",
633 "assert_eq!(cfgfifo.identify(\"path/to/file.ron\").unwrap(), Format::Json);\n",
634 "assert_eq!(cfgfifo.identify(\"path/to/file.cfg\").unwrap(), Format::Json);\n",
635 "assert_eq!(cfgfifo.identify(\"path/to/file\").unwrap(), Format::Json);\n",
636 "```\n",
637 ))]
638 pub fn identify<P: AsRef<Path>>(&self, path: P) -> Result<Format, IdentifyError> {
647 let ext = match (get_ext(path.as_ref()), self.fallback) {
648 (Ok(ext), _) => ext,
649 #[allow(unreachable_patterns)]
650 (Err(_), Some(f)) => return Ok(f),
651 (Err(e), _) => return Err(e),
652 };
653 self.formats
654 .iter()
655 .find(|f| f.has_extension(ext))
656 .copied()
657 .or(self.fallback)
658 .ok_or_else(|| IdentifyError::Unknown(ext.to_owned()))
659 }
660
661 pub fn load<T: DeserializeOwned, P: AsRef<Path>>(&self, path: P) -> Result<T, LoadError> {
670 let fmt = self.identify(&path)?;
671 let fp = io::BufReader::new(File::open(path).map_err(LoadError::Open)?);
672 fmt.load_from_reader(fp).map_err(Into::into)
673 }
674
675 pub fn dump<P: AsRef<Path>, T: Serialize>(&self, path: P, value: &T) -> Result<(), DumpError> {
684 let fmt = self.identify(&path)?;
685 let mut fp = io::BufWriter::new(File::create(path).map_err(DumpError::Open)?);
686 fmt.dump_to_writer(&mut fp, value)?;
687 fp.flush().map_err(DumpError::Flush)
688 }
689}
690
691impl Default for Cfgfifo {
692 fn default() -> Cfgfifo {
694 Cfgfifo::new()
695 }
696}
697
698#[derive(Clone, Debug, Eq, Error, PartialEq)]
700pub enum IdentifyError {
701 #[error("unknown file extension: {0:?}")]
704 Unknown(
705 String,
707 ),
708 #[error("file extension is not valid Unicode")]
710 NotUnicode,
711 #[error("file does not have a file extension")]
713 NoExtension,
714}
715
716#[derive(Debug, Error)]
723#[non_exhaustive]
724pub enum SerializeError {
725 #[error(transparent)]
729 Io(#[from] io::Error),
730
731 #[cfg(any(feature = "json", feature = "json5"))]
733 #[cfg_attr(docsrs, doc(cfg(any(feature = "json", feature = "json5"))))]
734 #[error(transparent)]
735 Json(#[from] PathError<serde_json::Error>),
736
737 #[cfg(feature = "ron")]
739 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
740 #[error(transparent)]
741 RonStart(ron::error::Error),
742
743 #[cfg(feature = "ron")]
745 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
746 #[error(transparent)]
747 Ron(#[from] PathError<ron::error::Error>),
748
749 #[cfg(feature = "toml")]
751 #[cfg_attr(docsrs, doc(cfg(feature = "toml")))]
752 #[error(transparent)]
753 Toml(#[from] PathError<toml::ser::Error>),
754
755 #[cfg(feature = "yaml")]
757 #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))]
758 #[error(transparent)]
759 Yaml(#[from] PathError<serde_yaml::Error>),
760}
761
762#[derive(Debug, Error)]
769#[non_exhaustive]
770pub enum DeserializeError {
771 #[error(transparent)]
775 Io(#[from] io::Error),
776
777 #[cfg(feature = "json")]
779 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
780 #[error(transparent)]
781 Json(#[from] PathError<serde_json::Error>),
782
783 #[cfg(feature = "json")]
785 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
786 #[error(transparent)]
787 JsonEnd(serde_json::Error),
788
789 #[cfg(feature = "json5")]
792 #[cfg_attr(docsrs, doc(cfg(feature = "json5")))]
793 #[error(transparent)]
794 Json5Syntax(json5::Error),
795
796 #[cfg(feature = "json5")]
798 #[cfg_attr(docsrs, doc(cfg(feature = "json5")))]
799 #[error(transparent)]
800 Json5(#[from] PathError<json5::Error>),
801
802 #[cfg(feature = "ron")]
804 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
805 #[error(transparent)]
806 RonStart(ron::error::SpannedError),
807
808 #[cfg(feature = "ron")]
810 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
811 #[error(transparent)]
812 Ron(#[from] PathError<ron::error::SpannedError>),
813
814 #[cfg(feature = "ron")]
816 #[cfg_attr(docsrs, doc(cfg(feature = "ron")))]
817 #[error(transparent)]
818 RonEnd(ron::error::SpannedError),
819
820 #[cfg(feature = "toml")]
822 #[cfg_attr(docsrs, doc(cfg(feature = "toml")))]
823 #[error(transparent)]
824 Toml(#[from] PathError<toml::de::Error>),
825
826 #[cfg(feature = "yaml")]
828 #[cfg_attr(docsrs, doc(cfg(feature = "yaml")))]
829 #[error(transparent)]
830 Yaml(#[from] PathError<serde_yaml::Error>),
831}
832
833#[derive(Debug, Error)]
835pub enum LoadError {
836 #[error("failed to identify file format")]
839 Identify(#[from] IdentifyError),
840
841 #[error("failed to open file for reading")]
843 Open(#[source] io::Error),
844
845 #[error("failed to deserialize file contents")]
847 Deserialize(#[from] DeserializeError),
848}
849
850#[derive(Debug, Error)]
852pub enum DumpError {
853 #[error("failed to identify file format")]
856 Identify(#[from] IdentifyError),
857
858 #[error("failed to open file for writing")]
860 Open(#[source] io::Error),
861
862 #[error("failed to serialize structure")]
864 Serialize(#[from] SerializeError),
865
866 #[error("failed to flush output file")]
868 Flush(#[source] io::Error),
869}
870
871#[cfg(feature = "ron")]
872fn ron_config() -> PrettyConfig {
873 PrettyConfig::default().new_line(String::from("\n"))
876}
877
878fn get_ext(path: &Path) -> Result<&str, IdentifyError> {
879 path.extension()
880 .ok_or(IdentifyError::NoExtension)?
881 .to_str()
882 .ok_or(IdentifyError::NotUnicode)
883}
884
885#[cfg(test)]
886mod tests {
887 use super::*;
888 use rstest::rstest;
889
890 #[rstest]
891 #[case("file.ini", "ini")]
892 #[case("file.xml", "xml")]
893 #[case("file.cfg", "cfg")]
894 #[case("file.jsn", "jsn")]
895 #[case("file.tml", "tml")]
896 fn identify_unknown(#[case] path: &str, #[case] ext: String) {
897 assert_eq!(
898 Format::identify(path),
899 Err(IdentifyError::Unknown(ext.clone()))
900 );
901 assert_eq!(
902 Cfgfifo::default().identify(path),
903 Err(IdentifyError::Unknown(ext))
904 );
905 }
906
907 #[cfg(unix)]
908 #[test]
909 fn identify_not_unicode() {
910 use std::os::unix::ffi::OsStrExt;
911 let path = std::ffi::OsStr::from_bytes(b"file.js\xF6n");
912 assert_eq!(Format::identify(path), Err(IdentifyError::NotUnicode));
913 assert_eq!(
914 Cfgfifo::default().identify(path),
915 Err(IdentifyError::NotUnicode)
916 );
917 }
918
919 #[cfg(windows)]
920 #[test]
921 fn identify_not_unicode() {
922 use std::os::windows::ffi::OsStringExt;
923 let path = std::ffi::OsString::from_wide(&[
924 0x66, 0x69, 0x6C, 0x65, 0x2E, 0x6A, 0xDC00, 0x73, 0x6E,
925 ]);
926 assert_eq!(Format::identify(&path), Err(IdentifyError::NotUnicode));
927 assert_eq!(
928 Cfgfifo::default().identify(path),
929 Err(IdentifyError::NotUnicode)
930 );
931 }
932
933 #[test]
934 fn identify_no_ext() {
935 assert_eq!(Format::identify("file"), Err(IdentifyError::NoExtension));
936 assert_eq!(
937 Cfgfifo::default().identify("file"),
938 Err(IdentifyError::NoExtension)
939 );
940 }
941
942 #[cfg(feature = "json")]
943 mod json {
944 use super::*;
945
946 #[test]
947 fn basics() {
948 let f = Format::Json;
949 assert_eq!(f.to_string(), "JSON");
950 assert_eq!(f.extensions(), ["json"]);
951 assert_eq!("json".parse::<Format>().unwrap(), f);
952 assert_eq!("JSON".parse::<Format>().unwrap(), f);
953 assert_eq!("Json".parse::<Format>().unwrap(), f);
954 assert!(Format::iter().any(|f2| f == f2));
955 }
956
957 #[rstest]
958 #[case("json")]
959 #[case(".json")]
960 #[case("JSON")]
961 #[case(".JSON")]
962 fn from_extension(#[case] ext: &str) {
963 assert!(Format::Json.has_extension(ext));
964 assert_eq!(Format::from_extension(ext).unwrap(), Format::Json);
965 }
966
967 #[rstest]
968 #[case("file.json")]
969 #[case("dir/file.JSON")]
970 #[case("/dir/file.Json")]
971 fn identify(#[case] path: &str) {
972 assert_eq!(Format::identify(path).unwrap(), Format::Json);
973 }
974 }
975
976 #[cfg(not(feature = "json"))]
977 mod not_json {
978 use super::*;
979
980 #[test]
981 fn not_variant() {
982 assert!(!Format::iter().any(|f| f.to_string() == "JSON"));
983 }
984
985 #[test]
986 fn identify() {
987 assert_eq!(
988 Format::identify("file.json"),
989 Err(IdentifyError::Unknown(String::from("json")))
990 );
991 }
992 }
993
994 #[cfg(feature = "json5")]
995 mod json5 {
996 use super::*;
997
998 #[test]
999 fn basics() {
1000 let f = Format::Json5;
1001 assert_eq!(f.to_string(), "JSON5");
1002 assert_eq!(f.extensions(), ["json5"]);
1003 assert_eq!("json5".parse::<Format>().unwrap(), f);
1004 assert_eq!("JSON5".parse::<Format>().unwrap(), f);
1005 assert_eq!("Json5".parse::<Format>().unwrap(), f);
1006 assert!(Format::iter().any(|f2| f == f2));
1007 }
1008
1009 #[rstest]
1010 #[case("json5")]
1011 #[case(".json5")]
1012 #[case("JSON5")]
1013 #[case(".JSON5")]
1014 fn from_extension(#[case] ext: &str) {
1015 assert!(Format::Json5.has_extension(ext));
1016 assert_eq!(Format::from_extension(ext).unwrap(), Format::Json5);
1017 }
1018
1019 #[rstest]
1020 #[case("file.json5")]
1021 #[case("dir/file.JSON5")]
1022 #[case("/dir/file.Json5")]
1023 fn identify(#[case] path: &str) {
1024 assert_eq!(Format::identify(path).unwrap(), Format::Json5);
1025 }
1026 }
1027
1028 #[cfg(not(feature = "json5"))]
1029 mod not_json5 {
1030 use super::*;
1031
1032 #[test]
1033 fn not_variant() {
1034 assert!(!Format::iter().any(|f| f.to_string() == "JSON5"));
1035 }
1036
1037 #[test]
1038 fn identify() {
1039 assert_eq!(
1040 Format::identify("file.json5"),
1041 Err(IdentifyError::Unknown(String::from("json5")))
1042 );
1043 }
1044 }
1045
1046 #[cfg(feature = "ron")]
1047 mod ron {
1048 use super::*;
1049
1050 #[test]
1051 fn basics() {
1052 let f = Format::Ron;
1053 assert_eq!(f.to_string(), "RON");
1054 assert_eq!(f.extensions(), ["ron"]);
1055 assert_eq!("ron".parse::<Format>().unwrap(), f);
1056 assert_eq!("RON".parse::<Format>().unwrap(), f);
1057 assert_eq!("Ron".parse::<Format>().unwrap(), f);
1058 assert!(Format::iter().any(|f2| f == f2));
1059 }
1060
1061 #[rstest]
1062 #[case("ron")]
1063 #[case(".ron")]
1064 #[case("RON")]
1065 #[case(".RON")]
1066 fn from_extension(#[case] ext: &str) {
1067 assert!(Format::Ron.has_extension(ext));
1068 assert_eq!(Format::from_extension(ext).unwrap(), Format::Ron);
1069 }
1070
1071 #[rstest]
1072 #[case("file.ron")]
1073 #[case("dir/file.RON")]
1074 #[case("/dir/file.Ron")]
1075 fn identify(#[case] path: &str) {
1076 assert_eq!(Format::identify(path).unwrap(), Format::Ron);
1077 }
1078 }
1079
1080 #[cfg(not(feature = "ron"))]
1081 mod not_ron {
1082 use super::*;
1083
1084 #[test]
1085 fn not_variant() {
1086 assert!(!Format::iter().any(|f| f.to_string() == "RON"));
1087 }
1088
1089 #[test]
1090 fn identify() {
1091 assert_eq!(
1092 Format::identify("file.ron"),
1093 Err(IdentifyError::Unknown(String::from("ron")))
1094 );
1095 }
1096 }
1097
1098 #[cfg(feature = "toml")]
1099 mod toml {
1100 use super::*;
1101
1102 #[test]
1103 fn basics() {
1104 let f = Format::Toml;
1105 assert_eq!(f.to_string(), "TOML");
1106 assert_eq!(f.extensions(), ["toml"]);
1107 assert_eq!("toml".parse::<Format>().unwrap(), f);
1108 assert_eq!("TOML".parse::<Format>().unwrap(), f);
1109 assert_eq!("Toml".parse::<Format>().unwrap(), f);
1110 assert!(Format::iter().any(|f2| f == f2));
1111 }
1112
1113 #[rstest]
1114 #[case("toml")]
1115 #[case(".toml")]
1116 #[case("TOML")]
1117 #[case(".TOML")]
1118 fn from_extension(#[case] ext: &str) {
1119 assert!(Format::Toml.has_extension(ext));
1120 assert_eq!(Format::from_extension(ext).unwrap(), Format::Toml);
1121 }
1122
1123 #[rstest]
1124 #[case("file.toml")]
1125 #[case("dir/file.TOML")]
1126 #[case("/dir/file.Toml")]
1127 fn identify(#[case] path: &str) {
1128 assert_eq!(Format::identify(path).unwrap(), Format::Toml);
1129 }
1130 }
1131
1132 #[cfg(not(feature = "toml"))]
1133 mod not_toml {
1134 use super::*;
1135
1136 #[test]
1137 fn not_variant() {
1138 assert!(!Format::iter().any(|f| f.to_string() == "TOML"));
1139 }
1140
1141 #[test]
1142 fn identify() {
1143 assert_eq!(
1144 Format::identify("file.toml"),
1145 Err(IdentifyError::Unknown(String::from("toml")))
1146 );
1147 }
1148 }
1149
1150 #[cfg(feature = "yaml")]
1151 mod yaml {
1152 use super::*;
1153
1154 #[test]
1155 fn basics() {
1156 let f = Format::Yaml;
1157 assert_eq!(f.to_string(), "YAML");
1158 assert_eq!(f.extensions(), ["yaml", "yml"]);
1159 assert_eq!("yaml".parse::<Format>().unwrap(), f);
1160 assert_eq!("YAML".parse::<Format>().unwrap(), f);
1161 assert_eq!("Yaml".parse::<Format>().unwrap(), f);
1162 assert!(Format::iter().any(|f2| f == f2));
1163 }
1164
1165 #[rstest]
1166 #[case("yaml")]
1167 #[case(".yaml")]
1168 #[case("YAML")]
1169 #[case(".YAML")]
1170 #[case("yml")]
1171 #[case(".yml")]
1172 #[case("YML")]
1173 #[case(".YML")]
1174 fn from_extension(#[case] ext: &str) {
1175 assert!(Format::Yaml.has_extension(ext));
1176 assert_eq!(Format::from_extension(ext).unwrap(), Format::Yaml);
1177 }
1178
1179 #[rstest]
1180 #[case("file.yaml")]
1181 #[case("dir/file.YAML")]
1182 #[case("/dir/file.Yaml")]
1183 #[case("file.yml")]
1184 #[case("dir/file.YML")]
1185 #[case("/dir/file.Yml")]
1186 fn identify(#[case] path: &str) {
1187 assert_eq!(Format::identify(path).unwrap(), Format::Yaml);
1188 }
1189 }
1190
1191 #[cfg(not(feature = "yaml"))]
1192 mod not_yaml {
1193 use super::*;
1194
1195 #[test]
1196 fn not_variant() {
1197 assert!(!Format::iter().any(|f| f.to_string() == "YAML"));
1198 }
1199
1200 #[test]
1201 fn identify() {
1202 assert_eq!(
1203 Format::identify("file.yaml"),
1204 Err(IdentifyError::Unknown(String::from("yaml")))
1205 );
1206 }
1207 }
1208
1209 mod cfgfifo {
1210 #[allow(unused_imports)]
1211 use super::*;
1212
1213 #[cfg(all(
1214 feature = "json",
1215 feature = "json5",
1216 feature = "ron",
1217 feature = "toml",
1218 feature = "yaml"
1219 ))]
1220 #[test]
1221 fn default() {
1222 let cfg = Cfgfifo::default();
1223 assert_eq!(cfg.identify("file.json").unwrap(), Format::Json);
1224 assert_eq!(cfg.identify("file.json5").unwrap(), Format::Json5);
1225 assert_eq!(cfg.identify("file.Ron").unwrap(), Format::Ron);
1226 assert_eq!(cfg.identify("file.toml").unwrap(), Format::Toml);
1227 assert_eq!(cfg.identify("file.YML").unwrap(), Format::Yaml);
1228 assert!(cfg.identify("file.cfg").is_err());
1229 assert!(cfg.identify("file").is_err());
1230 }
1231
1232 #[cfg(all(
1233 feature = "json",
1234 feature = "json5",
1235 feature = "ron",
1236 feature = "toml",
1237 feature = "yaml"
1238 ))]
1239 #[test]
1240 fn fallback() {
1241 let cfg = Cfgfifo::new().fallback(Some(Format::Json));
1242 assert_eq!(cfg.identify("file.json").unwrap(), Format::Json);
1243 assert_eq!(cfg.identify("file.json5").unwrap(), Format::Json5);
1244 assert_eq!(cfg.identify("file.Ron").unwrap(), Format::Ron);
1245 assert_eq!(cfg.identify("file.toml").unwrap(), Format::Toml);
1246 assert_eq!(cfg.identify("file.YML").unwrap(), Format::Yaml);
1247 assert_eq!(cfg.identify("file.cfg").unwrap(), Format::Json);
1248 assert_eq!(cfg.identify("file").unwrap(), Format::Json);
1249 }
1250
1251 #[cfg(all(feature = "json", feature = "toml"))]
1252 #[test]
1253 fn formats() {
1254 let cfg = Cfgfifo::new().formats([Format::Json, Format::Toml]);
1255 assert_eq!(cfg.identify("file.json").unwrap(), Format::Json);
1256 assert!(cfg.identify("file.json5").is_err());
1257 assert!(cfg.identify("file.Ron").is_err());
1258 assert_eq!(cfg.identify("file.toml").unwrap(), Format::Toml);
1259 assert!(cfg.identify("file.YML").is_err());
1260 assert!(cfg.identify("file.cfg").is_err());
1261 assert!(cfg.identify("file").is_err());
1262 }
1263
1264 #[cfg(all(feature = "json", feature = "toml", feature = "yaml"))]
1265 #[test]
1266 fn formats_fallback() {
1267 let cfg = Cfgfifo::new()
1268 .formats([Format::Json, Format::Toml])
1269 .fallback(Some(Format::Yaml));
1270 assert_eq!(cfg.identify("file.json").unwrap(), Format::Json);
1271 assert_eq!(cfg.identify("file.json5").unwrap(), Format::Yaml);
1272 assert_eq!(cfg.identify("file.Ron").unwrap(), Format::Yaml);
1273 assert_eq!(cfg.identify("file.toml").unwrap(), Format::Toml);
1274 assert_eq!(cfg.identify("file.YML").unwrap(), Format::Yaml);
1275 assert_eq!(cfg.identify("file.cfg").unwrap(), Format::Yaml);
1276 assert_eq!(cfg.identify("file").unwrap(), Format::Yaml);
1277 }
1278 }
1279}