Skip to main content

rskit_codec/
select.rs

1//! Runtime codec selection by file extension or name.
2//!
3//! Returns an `Arc<dyn Codec>`
4//! so a caller (a file sink, a document loader) can pick a codec at runtime from a path's extension without knowing the concrete type.
5//! Only the codecs compiled in are selectable: the TOML codec requires the default-on `toml` feature;
6//! JSON and YAML are always available.
7
8use std::path::Path;
9use std::sync::Arc;
10
11use crate::JsonCodec;
12#[cfg(feature = "toml")]
13use crate::TomlCodec;
14use crate::YamlCodec;
15use crate::codec::Codec;
16
17/// Return a codec for a lowercase format `name` (for example `"toml"`, `"json"`, `"yaml"`),
18/// or `None` when no compiled-in codec matches.
19#[must_use]
20pub fn codec_for_name(name: &str) -> Option<Arc<dyn Codec>> {
21    match name.to_ascii_lowercase().as_str() {
22        #[cfg(feature = "toml")]
23        "toml" => Some(Arc::new(TomlCodec)),
24        "json" => Some(Arc::new(JsonCodec::default())),
25        "yaml" | "yml" => Some(Arc::new(YamlCodec)),
26        _ => None,
27    }
28}
29
30/// Return a codec for `path`'s file extension, or `None` when the extension is missing or unrecognized.
31#[must_use]
32pub fn codec_for_path(path: &Path) -> Option<Arc<dyn Codec>> {
33    let ext = path.extension().and_then(|ext| ext.to_str())?;
34    codec_for_name(ext)
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn selects_json_by_name_and_path() {
43        assert_eq!(codec_for_name("json").unwrap().name(), "json");
44        assert_eq!(codec_for_name("JSON").unwrap().name(), "json");
45        assert_eq!(
46            codec_for_path(Path::new("/etc/app/config.json"))
47                .unwrap()
48                .name(),
49            "json"
50        );
51    }
52
53    #[cfg(feature = "toml")]
54    #[test]
55    fn selects_toml_by_name_and_path() {
56        assert_eq!(codec_for_name("toml").unwrap().name(), "toml");
57        assert_eq!(
58            codec_for_path(Path::new("config.toml")).unwrap().name(),
59            "toml"
60        );
61    }
62
63    #[test]
64    fn selects_yaml_by_name_and_path() {
65        assert_eq!(codec_for_name("yaml").unwrap().name(), "yaml");
66        assert_eq!(codec_for_name("yml").unwrap().name(), "yaml");
67        assert_eq!(
68            codec_for_path(Path::new("config.yaml")).unwrap().name(),
69            "yaml"
70        );
71        assert_eq!(
72            codec_for_path(Path::new("config.yml")).unwrap().name(),
73            "yaml"
74        );
75    }
76
77    #[test]
78    fn returns_none_for_unknown_or_missing_extension() {
79        assert!(codec_for_name("ini").is_none());
80        assert!(codec_for_path(Path::new("config")).is_none());
81    }
82}