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 is always available.
7
8use std::path::Path;
9use std::sync::Arc;
10
11use crate::JsonCodec;
12#[cfg(feature = "toml")]
13use crate::TomlCodec;
14use crate::codec::Codec;
15
16/// Return a codec for a lowercase format `name` (for example `"toml"`, `"json"`),
17/// or `None` when no compiled-in codec matches.
18#[must_use]
19pub fn codec_for_name(name: &str) -> Option<Arc<dyn Codec>> {
20    match name.to_ascii_lowercase().as_str() {
21        #[cfg(feature = "toml")]
22        "toml" => Some(Arc::new(TomlCodec)),
23        "json" => Some(Arc::new(JsonCodec::default())),
24        _ => None,
25    }
26}
27
28/// Return a codec for `path`'s file extension, or `None` when the extension is missing or unrecognized.
29#[must_use]
30pub fn codec_for_path(path: &Path) -> Option<Arc<dyn Codec>> {
31    let ext = path.extension().and_then(|ext| ext.to_str())?;
32    codec_for_name(ext)
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn selects_json_by_name_and_path() {
41        assert_eq!(codec_for_name("json").unwrap().name(), "json");
42        assert_eq!(codec_for_name("JSON").unwrap().name(), "json");
43        assert_eq!(
44            codec_for_path(Path::new("/etc/app/config.json"))
45                .unwrap()
46                .name(),
47            "json"
48        );
49    }
50
51    #[cfg(feature = "toml")]
52    #[test]
53    fn selects_toml_by_name_and_path() {
54        assert_eq!(codec_for_name("toml").unwrap().name(), "toml");
55        assert_eq!(
56            codec_for_path(Path::new("config.toml")).unwrap().name(),
57            "toml"
58        );
59    }
60
61    #[test]
62    fn returns_none_for_unknown_or_missing_extension() {
63        assert!(codec_for_name("yaml").is_none());
64        assert!(codec_for_path(Path::new("config")).is_none());
65    }
66}