Skip to main content

wsi_rs/core/registry/
registry_impl.rs

1use super::*;
2
3// ── Arc blanket impls ─────────────────────────────────────────────
4// Enable a single Arc<T> to be registered as both FormatProbe and
5// DatasetReader when T implements both traits. Used by TiffFamilyBackend.
6
7impl<T: FormatProbe> FormatProbe for Arc<T> {
8    fn probe(&self, path: &Path) -> Result<ProbeResult, WsiError> {
9        (**self).probe(path)
10    }
11}
12
13impl<T: DatasetReader> DatasetReader for Arc<T> {
14    fn open(&self, path: &Path) -> Result<Box<dyn SlideReader>, WsiError> {
15        (**self).open(path)
16    }
17}
18
19// ── Format registry ────────────────────────────────────────────────
20
21#[derive(Default)]
22pub struct FormatRegistry {
23    backends: Vec<RegisteredBackend>,
24}
25
26impl std::fmt::Debug for FormatRegistry {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.debug_struct("FormatRegistry")
29            .field("backend_count", &self.backends.len())
30            .finish()
31    }
32}
33
34struct RegisteredBackend {
35    probe: Box<dyn FormatProbe>,
36    reader: Box<dyn DatasetReader>,
37}
38
39impl FormatRegistry {
40    pub fn new() -> Self {
41        Self::default()
42    }
43
44    pub fn register(
45        &mut self,
46        probe: impl FormatProbe + 'static,
47        reader: impl DatasetReader + 'static,
48    ) {
49        self.backends.push(RegisteredBackend {
50            probe: Box::new(probe),
51            reader: Box::new(reader),
52        });
53    }
54
55    /// Create a registry with all built-in backends registered.
56    pub fn builtin() -> Self {
57        let mut reg = Self::new();
58        let svcache = Arc::new(SvcacheBackend::new());
59        reg.register(svcache.clone(), svcache);
60        reg.register_native_backends();
61        reg
62    }
63
64    pub(crate) fn builtin_native() -> Self {
65        let mut reg = Self::new();
66        reg.register_native_backends();
67        reg
68    }
69
70    fn register_native_backends(&mut self) {
71        let dicom = Arc::new(DicomBackend::new());
72        self.register(dicom.clone(), dicom);
73        let mirax = Arc::new(MiraxBackend::new());
74        self.register(mirax.clone(), mirax);
75        let vms = Arc::new(HamamatsuVmsBackend::new());
76        self.register(vms.clone(), vms);
77        let vsi = Arc::new(OlympusVsiBackend::new());
78        self.register(vsi.clone(), vsi);
79        let raw_jp2k = Arc::new(RawJp2kBackend::new());
80        self.register(raw_jp2k.clone(), raw_jp2k);
81        let zeiss_zvi = Arc::new(ZeissZviBackend::new());
82        self.register(zeiss_zvi.clone(), zeiss_zvi);
83        let zeiss = Arc::new(ZeissBackend::new());
84        self.register(zeiss.clone(), zeiss);
85        let tiff = Arc::new(TiffFamilyBackend::new());
86        self.register(tiff.clone(), tiff);
87    }
88
89    /// Probe all backends and return the best detected format without opening it.
90    ///
91    /// Definite confidence beats Likely. First-registered wins ties.
92    pub fn detect_vendor(&self, path: &Path) -> Result<Option<ProbeResult>, WsiError> {
93        self.best_probe(path)
94            .map(|best| best.map(|(result, _)| result))
95    }
96
97    /// Probe all backends, open with best match.
98    /// Definite confidence beats Likely. First-registered wins ties.
99    pub fn open(&self, path: &Path) -> Result<Box<dyn SlideReader>, WsiError> {
100        self.open_exact(path)
101    }
102
103    pub(crate) fn open_exact(&self, path: &Path) -> Result<Box<dyn SlideReader>, WsiError> {
104        match self.best_probe(path)? {
105            Some((_, i)) => self.backends[i].reader.open(path),
106            None => Err(WsiError::UnsupportedFormat(path.display().to_string())),
107        }
108    }
109
110    fn best_probe(&self, path: &Path) -> Result<Option<(ProbeResult, usize)>, WsiError> {
111        let mut best: Option<(ProbeResult, usize)> = None;
112        let mut first_error: Option<WsiError> = None;
113
114        for (i, backend) in self.backends.iter().enumerate() {
115            match backend.probe.probe(path) {
116                Ok(result) => {
117                    if result.detected {
118                        let should_replace = match best.as_ref() {
119                            None => true,
120                            Some((existing, _)) => {
121                                existing.confidence == ProbeConfidence::Likely
122                                    && result.confidence == ProbeConfidence::Definite
123                            }
124                        };
125                        if should_replace {
126                            best = Some((result, i));
127                        }
128                    }
129                }
130                Err(err) => {
131                    if first_error.is_none() {
132                        first_error = Some(err);
133                    }
134                }
135            }
136        }
137
138        match best {
139            Some(best) => Ok(Some(best)),
140            None => {
141                if let Some(err) = first_error {
142                    Err(err)
143                } else {
144                    Ok(None)
145                }
146            }
147        }
148    }
149}