1use std::collections::HashMap;
2use std::sync::Arc;
3
4use crate::codec::Codec;
5use crate::PatchError;
6
7pub 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 pub fn new() -> Self {
17 Self {
18 codecs: HashMap::new(),
19 extension_map: HashMap::new(),
20 fallback: None,
21 }
22 }
23
24 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 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 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 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 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 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}