use std::path::{Path, PathBuf};
use crate::common::error::{BioFormatsError, Result};
use crate::common::metadata::ImageMetadata;
use crate::common::reader::FormatReader;
const LIF_MAGIC: u8 = 0x70;
const LIF_UNSUPPORTED: &str =
"Leica LIF decoding is incomplete; disabled until metadata and plane layout are ported";
pub struct LifReader {
path: Option<PathBuf>,
meta: ImageMetadata,
current_series: usize,
}
impl LifReader {
pub fn new() -> Self {
LifReader {
path: None,
meta: ImageMetadata::default(),
current_series: 0,
}
}
fn unsupported() -> BioFormatsError {
BioFormatsError::UnsupportedFormat(LIF_UNSUPPORTED.to_string())
}
}
impl Default for LifReader {
fn default() -> Self {
Self::new()
}
}
impl FormatReader for LifReader {
fn is_this_type_by_name(&self, path: &Path) -> bool {
path.extension()
.and_then(|e| e.to_str())
.map(|e| e.eq_ignore_ascii_case("lif"))
.unwrap_or(false)
}
fn is_this_type_by_bytes(&self, _header: &[u8]) -> bool {
false
}
fn set_id(&mut self, path: &Path) -> Result<()> {
self.path = None;
self.current_series = 0;
self.meta = ImageMetadata::default();
if !self.is_this_type_by_name(path) {
let header = std::fs::read(path).map_err(BioFormatsError::Io)?;
if header.first().copied() != Some(LIF_MAGIC) {
return Err(BioFormatsError::Format("Not a Leica LIF file".into()));
}
}
Err(Self::unsupported())
}
fn close(&mut self) -> Result<()> {
self.path = None;
self.meta = ImageMetadata::default();
self.current_series = 0;
Ok(())
}
fn series_count(&self) -> usize {
0
}
fn set_series(&mut self, series: usize) -> Result<()> {
Err(BioFormatsError::SeriesOutOfRange(series))
}
fn series(&self) -> usize {
self.current_series
}
fn metadata(&self) -> &ImageMetadata {
crate::common::reader::uninitialized_metadata()
}
fn open_bytes(&mut self, _plane_index: u32) -> Result<Vec<u8>> {
Err(Self::unsupported())
}
fn open_bytes_region(
&mut self,
_plane_index: u32,
_x: u32,
_y: u32,
_w: u32,
_h: u32,
) -> Result<Vec<u8>> {
Err(Self::unsupported())
}
fn open_thumb_bytes(&mut self, _plane_index: u32) -> Result<Vec<u8>> {
Err(Self::unsupported())
}
}
#[cfg(test)]
mod tests {
use super::{LifReader, LIF_MAGIC, LIF_UNSUPPORTED};
use crate::common::error::BioFormatsError;
use crate::common::reader::FormatReader;
use crate::ImageReader;
use std::path::PathBuf;
fn temp_path(name: &str) -> PathBuf {
std::env::temp_dir().join(format!("bioformats_lif_{name}"))
}
fn assert_lif_unsupported(err: BioFormatsError) {
assert!(
matches!(err, BioFormatsError::UnsupportedFormat(ref message) if message.contains(LIF_UNSUPPORTED)),
"expected LIF unsupported error, got {err:?}"
);
}
#[test]
fn invalid_lif_extension_rejects_without_metadata_panic() {
let path = temp_path("tiny_invalid.lif");
std::fs::write(&path, b"not a real lif").unwrap();
let mut reader = LifReader::new();
assert_lif_unsupported(reader.set_id(&path).unwrap_err());
assert_eq!(reader.metadata().size_x, 0);
assert_lif_unsupported(reader.open_bytes(0).unwrap_err());
let _ = std::fs::remove_file(path);
}
#[test]
fn image_reader_does_not_open_tiny_lif_with_fake_metadata() {
let path = temp_path("tiny_registry.lif");
std::fs::write(&path, [LIF_MAGIC, 0, 0, 0, 0]).unwrap();
let err = match ImageReader::open(&path) {
Ok(_) => panic!("LIF reader opened fake metadata"),
Err(err) => err,
};
assert_lif_unsupported(err);
let _ = std::fs::remove_file(path);
}
}