Files
rustytorch/demos/slidescope-shared/src/slide.rs
T
osobhandClaude Opus 4.6 02d382d5f6 style: apply rustfmt across all crates and demos
Consistent formatting pass: line wrapping, import sorting, trailing
whitespace removal, let-chain indentation, merged derive attributes,
and unsafe block reformatting.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-12 07:01:58 -07:00

148 lines
4.1 KiB
Rust

//! Slide metadata types for SlideScope demo
use serde::{Deserialize, Serialize};
/// Slide metadata containing all information about a pathology slide
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SlideMetadata {
/// Unique slide identifier
pub id: String,
/// Display name of the slide
pub name: String,
/// Original file path
pub path: String,
/// Image width in pixels
pub width: u32,
/// Image height in pixels
pub height: u32,
/// Current processing status
pub status: SlideStatus,
/// Detected or configured stain type
pub stain_type: Option<StainType>,
/// Path to generated thumbnail
pub thumbnail_path: Option<String>,
/// Path to pre-generated tile pyramid
pub pyramid_path: Option<String>,
/// Unix timestamp when slide was imported
pub created_at: i64,
/// Unix timestamp when processing completed
pub processed_at: Option<i64>,
}
/// Processing status of a slide
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[derive(Default)]
pub enum SlideStatus {
/// Slide imported and pyramid generated, ready for processing
#[default]
Imported,
/// NMF processing in progress
Processing,
/// Processing completed successfully
Processed,
/// Processing failed
Failed,
}
impl std::fmt::Display for SlideStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Imported => write!(f, "imported"),
Self::Processing => write!(f, "processing"),
Self::Processed => write!(f, "processed"),
Self::Failed => write!(f, "failed"),
}
}
}
/// Stain type for pathology slides
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum StainType {
/// Hematoxylin and Eosin (most common)
#[serde(rename = "H&E")]
#[default]
HE,
/// Immunohistochemistry with DAB chromogen
#[serde(rename = "IHC-DAB")]
IhcDab,
/// Custom or unknown stain configuration
Custom,
}
impl std::fmt::Display for StainType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::HE => write!(f, "H&E"),
Self::IhcDab => write!(f, "IHC-DAB"),
Self::Custom => write!(f, "Custom"),
}
}
}
/// Supported image formats for import
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ImageFormat {
/// Standard TIFF
Tiff,
/// BigTIFF for large images
BigTiff,
/// Aperio SVS format
Svs,
/// Hamamatsu NDPI format
Ndpi,
/// PNG format
Png,
/// JPEG format
Jpeg,
}
impl ImageFormat {
/// Detect format from file extension
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"tif" | "tiff" => Some(Self::Tiff),
"svs" => Some(Self::Svs),
"ndpi" => Some(Self::Ndpi),
"png" => Some(Self::Png),
"jpg" | "jpeg" => Some(Self::Jpeg),
_ => None,
}
}
/// Check if format requires OpenSlide
pub fn requires_openslide(&self) -> bool {
matches!(self, Self::Svs | Self::Ndpi)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slide_status_serialization() {
let status = SlideStatus::Processing;
let json = serde_json::to_string(&status).unwrap();
assert_eq!(json, "\"processing\"");
let deserialized: SlideStatus = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, SlideStatus::Processing);
}
#[test]
fn test_stain_type_serialization() {
let stain = StainType::HE;
let json = serde_json::to_string(&stain).unwrap();
assert_eq!(json, "\"H&E\"");
}
#[test]
fn test_image_format_detection() {
assert_eq!(ImageFormat::from_extension("tif"), Some(ImageFormat::Tiff));
assert_eq!(ImageFormat::from_extension("SVS"), Some(ImageFormat::Svs));
assert_eq!(ImageFormat::from_extension("unknown"), None);
}
}