Skip to main content

rskit_codec/
select.rs

1//! Runtime codec selection by file extension or name.
2//!
3//! Returns an `Arc<dyn Codec>` so a caller (a file sink, a document loader) can
4//! pick a codec at runtime from a path's extension without knowing the concrete
5//! type. Only the codecs compiled in are selectable: the TOML codec requires the
6//! default-on `toml` feature; 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"`,
17/// `"json"`), 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)),
24        _ => None,
25    }
26}
27
28/// Return a codec for `path`'s file extension, or `None` when the extension is
29/// missing or unrecognized.
30#[must_use]
31pub fn codec_for_path(path: &Path) -> Option<Arc<dyn Codec>> {
32    let ext = path.extension().and_then(|ext| ext.to_str())?;
33    codec_for_name(ext)
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn selects_json_by_name_and_path() {
42        assert_eq!(codec_for_name("json").unwrap().name(), "json");
43        assert_eq!(codec_for_name("JSON").unwrap().name(), "json");
44        assert_eq!(
45            codec_for_path(Path::new("/etc/app/config.json"))
46                .unwrap()
47                .name(),
48            "json"
49        );
50    }
51
52    #[cfg(feature = "toml")]
53    #[test]
54    fn selects_toml_by_name_and_path() {
55        assert_eq!(codec_for_name("toml").unwrap().name(), "toml");
56        assert_eq!(
57            codec_for_path(Path::new("config.toml")).unwrap().name(),
58            "toml"
59        );
60    }
61
62    #[test]
63    fn returns_none_for_unknown_or_missing_extension() {
64        assert!(codec_for_name("yaml").is_none());
65        assert!(codec_for_path(Path::new("config")).is_none());
66    }
67}