Skip to main content

claw_patch/
registry.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use crate::codec::Codec;
5use crate::PatchError;
6
7/// Registry for resolving codecs by stable id, file extension, or fallback.
8pub struct CodecRegistry {
9    codecs: HashMap<String, Arc<dyn Codec>>,
10    extension_map: HashMap<String, String>,
11    fallback: Option<Arc<dyn Codec>>,
12}
13
14impl CodecRegistry {
15    /// Create an empty registry.
16    pub fn new() -> Self {
17        Self {
18            codecs: HashMap::new(),
19            extension_map: HashMap::new(),
20            fallback: None,
21        }
22    }
23
24    /// Register a codec and map the provided extensions to its id.
25    pub fn register(&mut self, codec: Arc<dyn Codec>, extensions: &[&str]) {
26        let id = codec.id().to_string();
27        for ext in extensions {
28            self.extension_map.insert(ext.to_string(), id.clone());
29        }
30        self.codecs.insert(id, codec);
31    }
32
33    /// Register the fallback codec used when no extension-specific codec matches.
34    pub fn set_fallback(&mut self, codec: Arc<dyn Codec>) {
35        let id = codec.id().to_string();
36        self.codecs.insert(id, codec.clone());
37        self.fallback = Some(codec);
38    }
39
40    /// Get a codec by stable id.
41    pub fn get(&self, codec_id: &str) -> Result<&Arc<dyn Codec>, PatchError> {
42        self.codecs
43            .get(codec_id)
44            .ok_or_else(|| PatchError::CodecNotFound(codec_id.to_string()))
45    }
46
47    /// Get a codec by file extension without the leading dot.
48    pub fn get_by_extension(&self, ext: &str) -> Option<&Arc<dyn Codec>> {
49        let codec_id = self.extension_map.get(ext)?;
50        self.codecs.get(codec_id)
51    }
52
53    /// Get the best codec for a file path, falling back when configured.
54    pub fn get_for_path(&self, path: &str) -> Option<&Arc<dyn Codec>> {
55        let ext = path.rsplit('.').next().unwrap_or("");
56        self.get_by_extension(ext).or(self.fallback.as_ref())
57    }
58
59    /// Build the default registry with text, JSON, and binary codecs.
60    pub fn default_registry() -> Self {
61        use crate::binary::BinaryCodec;
62        use crate::json_tree::JsonTreeCodec;
63        use crate::text_line::TextLineCodec;
64
65        let mut reg = Self::new();
66        reg.register(
67            Arc::new(TextLineCodec),
68            &[
69                "txt", "md", "rs", "py", "js", "ts", "c", "h", "cpp", "go", "rb", "sh", "toml",
70                "yaml", "yml",
71            ],
72        );
73        reg.register(Arc::new(JsonTreeCodec), &["json"]);
74        reg.set_fallback(Arc::new(BinaryCodec));
75        reg
76    }
77}
78
79impl Default for CodecRegistry {
80    fn default() -> Self {
81        Self::default_registry()
82    }
83}