//! Shared IPC types for CellAtlas single-cell transcriptomics demo. //! //! This crate provides data structures for communication between //! the Tauri frontend and Rust backend for single-cell analysis. use serde::{Deserialize, Serialize}; // ============================================================================ // Cell Data Types // ============================================================================ /// A single cell with gene expression data. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Cell { /// Unique cell identifier pub id: String, /// Cell barcode (from sequencing) pub barcode: Option, /// Gene expression values (sparse representation) pub expression: SparseExpression, /// Assigned cell type pub cell_type: Option, /// Cell state annotations pub state: Option, /// Quality metrics pub qc_metrics: QualityMetrics, /// Spatial coordinates (if spatial transcriptomics) pub spatial_coords: Option, /// Cluster assignment pub cluster_id: Option, /// UMAP/t-SNE embedding coordinates pub embedding: Option, } /// Sparse gene expression representation. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SparseExpression { /// Gene indices (into a gene name list) pub gene_indices: Vec, /// Expression values (counts or normalized) pub values: Vec, /// Total number of genes in reference pub num_genes: usize, } impl SparseExpression { /// Get expression value for a gene index. pub fn get(&self, gene_idx: usize) -> f32 { self.gene_indices .iter() .position(|&idx| idx == gene_idx) .map_or(0.0, |pos| self.values[pos]) } /// Get the number of expressed genes. pub fn num_expressed(&self) -> usize { self.values.iter().filter(|&&v| v > 0.0).count() } /// Get total UMI counts. pub fn total_counts(&self) -> f32 { self.values.iter().sum() } } /// 2D embedding coordinates (UMAP, t-SNE, etc.). #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct Embedding2D { pub x: f32, pub y: f32, } /// Spatial coordinates for spatial transcriptomics. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct SpatialCoords { /// X coordinate in tissue space pub x: f32, /// Y coordinate in tissue space pub y: f32, /// Z coordinate (for 3D spatial data) pub z: Option, /// Tissue section/slice ID pub section_id: Option, } // ============================================================================ // Cell Type Annotation // ============================================================================ /// Cell type annotation. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CellType { /// Primary cell type name pub name: String, /// Confidence score (0-1) pub confidence: f32, /// Hierarchical lineage (e.g., ["Immune", "T cell", "CD8+ T cell"]) pub lineage: Vec, /// Marker genes used for annotation pub markers: Vec, } /// Cell state annotation (activation, cycling, etc.). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CellState { /// State name (e.g., "Activated", "Resting", "Cycling") pub name: String, /// State score (0-1) pub score: f32, /// Cell cycle phase (if applicable) pub cell_cycle_phase: Option, } /// Cell cycle phase. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub enum CellCyclePhase { G1, S, G2, M, G0, } /// Quality control metrics for a cell. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QualityMetrics { /// Number of genes detected pub n_genes: usize, /// Total UMI counts pub total_counts: f32, /// Percentage of mitochondrial reads pub pct_mito: f32, /// Percentage of ribosomal reads pub pct_ribo: f32, /// Doublet score (0-1, higher = more likely doublet) pub doublet_score: Option, } // ============================================================================ // Dataset Types // ============================================================================ /// Single-cell dataset. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SingleCellDataset { /// Dataset name pub name: String, /// Dataset description pub description: Option, /// List of gene names pub gene_names: Vec, /// Number of cells pub n_cells: usize, /// Number of genes pub n_genes: usize, /// Data modality pub modality: DataModality, /// Organism pub organism: Organism, /// Tissue/sample source pub tissue: Option, } /// Data modality. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum DataModality { /// Single-cell RNA sequencing ScRnaSeq, /// Single-nucleus RNA sequencing SnRnaSeq, /// Spatial transcriptomics (Visium, etc.) SpatialTranscriptomics, /// CITE-seq (RNA + protein) CiteSeq, /// Single-cell ATAC-seq ScAtacSeq, /// Multi-modal (multiple modalities) MultiModal, } /// Organism. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Organism { Human, Mouse, Rat, Zebrafish, Drosophila, Other, } // ============================================================================ // Analysis Request/Response Types // ============================================================================ /// Request to analyze a single-cell dataset. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AnalysisRequest { /// Dataset to analyze pub dataset_id: String, /// Analysis configuration pub config: AnalysisConfig, } /// Analysis configuration. #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct AnalysisConfig { /// Preprocessing options pub preprocessing: PreprocessingConfig, /// Dimensionality reduction pub dim_reduction: DimReductionConfig, /// Clustering options pub clustering: ClusteringConfig, /// Cell type annotation options pub annotation: AnnotationConfig, } /// Preprocessing configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PreprocessingConfig { /// Minimum genes per cell pub min_genes: usize, /// Minimum cells per gene pub min_cells: usize, /// Maximum mitochondrial percentage pub max_mito_pct: f32, /// Normalization method pub normalization: NormalizationMethod, /// Number of highly variable genes pub n_hvg: usize, } impl Default for PreprocessingConfig { fn default() -> Self { Self { min_genes: 200, min_cells: 3, max_mito_pct: 20.0, normalization: NormalizationMethod::LogNormalize, n_hvg: 2000, } } } /// Normalization method. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum NormalizationMethod { /// Log normalization (Seurat/Scanpy default) LogNormalize, /// SCTransform (variance stabilizing) ScTransform, /// Counts per million Cpm, /// No normalization (use raw counts) None, } /// Dimensionality reduction configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DimReductionConfig { /// Number of PCA components pub n_pcs: usize, /// Method for 2D embedding pub embedding_method: EmbeddingMethod, /// Number of neighbors for UMAP/t-SNE pub n_neighbors: usize, /// Minimum distance for UMAP pub min_dist: f32, } impl Default for DimReductionConfig { fn default() -> Self { Self { n_pcs: 50, embedding_method: EmbeddingMethod::Umap, n_neighbors: 15, min_dist: 0.1, } } } /// Embedding method for visualization. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum EmbeddingMethod { Umap, Tsne, Pca, ForceDirected, } /// Clustering configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClusteringConfig { /// Clustering algorithm pub algorithm: ClusteringAlgorithm, /// Resolution parameter (for Leiden/Louvain) pub resolution: f32, /// Number of clusters (for k-means) pub n_clusters: Option, } impl Default for ClusteringConfig { fn default() -> Self { Self { algorithm: ClusteringAlgorithm::Leiden, resolution: 1.0, n_clusters: None, } } } /// Clustering algorithm. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ClusteringAlgorithm { Leiden, Louvain, Kmeans, Hierarchical, } /// Cell type annotation configuration. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AnnotationConfig { /// Annotation method pub method: AnnotationMethod, /// Reference dataset for transfer learning pub reference: Option, /// Marker gene sets pub marker_genes: Option>, } impl Default for AnnotationConfig { fn default() -> Self { Self { method: AnnotationMethod::MarkerBased, reference: None, marker_genes: None, } } } /// Annotation method. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum AnnotationMethod { /// Manual marker-based annotation MarkerBased, /// Transfer learning from reference TransferLearning, /// Foundation model (CellAtlas transformer) FoundationModel, } /// Marker gene set for a cell type. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MarkerGeneSet { /// Cell type name pub cell_type: String, /// Positive markers (must be expressed) pub positive_markers: Vec, /// Negative markers (must not be expressed) pub negative_markers: Vec, } // ============================================================================ // Analysis Results // ============================================================================ /// Analysis results. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AnalysisResult { /// Cluster assignments pub clusters: Vec, /// Cell type annotations pub cell_types: Vec, /// Differential expression results pub de_results: Option>, /// Spatial niche analysis (if applicable) pub spatial_niches: Option>, /// Quality summary pub qc_summary: QcSummary, } /// Cluster information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClusterInfo { /// Cluster ID pub id: usize, /// Number of cells pub n_cells: usize, /// Top marker genes pub markers: Vec, /// Centroid in embedding space pub centroid: Option, } /// Cell type annotation result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CellTypeAnnotation { /// Cell type name pub name: String, /// Number of cells pub n_cells: usize, /// Percentage of total pub percentage: f32, /// Average confidence pub avg_confidence: f32, /// Clusters with this cell type pub clusters: Vec, } /// Differential expression result. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DifferentialExpression { /// Gene name pub gene: String, /// Log2 fold change pub log2fc: f32, /// P-value pub pvalue: f32, /// Adjusted p-value (FDR) pub padj: f32, /// Percentage expressed in group 1 pub pct1: f32, /// Percentage expressed in group 2 pub pct2: f32, } /// Spatial niche information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SpatialNiche { /// Niche ID pub id: usize, /// Niche name/description pub name: String, /// Cell types in this niche pub cell_types: Vec, /// Cell type proportions pub proportions: Vec, /// Spatial center pub center: SpatialCoords, } /// QC summary statistics. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QcSummary { /// Total cells before filtering pub n_cells_before: usize, /// Total cells after filtering pub n_cells_after: usize, /// Total genes before filtering pub n_genes_before: usize, /// Total genes after filtering pub n_genes_after: usize, /// Median genes per cell pub median_genes: f32, /// Median counts per cell pub median_counts: f32, } // ============================================================================ // Sample Data // ============================================================================ /// Sample dataset for demo. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SampleDataset { /// Dataset name pub name: String, /// Description pub description: String, /// Number of cells pub n_cells: usize, /// Modality pub modality: DataModality, /// Tissue type pub tissue: String, /// Organism pub organism: Organism, } /// Get available sample datasets. pub fn get_sample_datasets() -> Vec { vec![ SampleDataset { name: "PBMC 3k".to_string(), description: "3,000 peripheral blood mononuclear cells from 10x Genomics".to_string(), n_cells: 2700, modality: DataModality::ScRnaSeq, tissue: "Blood".to_string(), organism: Organism::Human, }, SampleDataset { name: "Mouse Brain Spatial".to_string(), description: "Spatial transcriptomics of mouse brain cortex".to_string(), n_cells: 3500, modality: DataModality::SpatialTranscriptomics, tissue: "Brain".to_string(), organism: Organism::Mouse, }, SampleDataset { name: "Tumor Microenvironment".to_string(), description: "Single-cell analysis of tumor-infiltrating immune cells".to_string(), n_cells: 8000, modality: DataModality::ScRnaSeq, tissue: "Tumor".to_string(), organism: Organism::Human, }, SampleDataset { name: "Developing Heart".to_string(), description: "Multi-nuclei RNA-seq of developing human heart".to_string(), n_cells: 5000, modality: DataModality::SnRnaSeq, tissue: "Heart".to_string(), organism: Organism::Human, }, ] } /// Common marker genes for major cell types. pub fn get_common_markers() -> Vec { vec![ MarkerGeneSet { cell_type: "T cell".to_string(), positive_markers: vec!["CD3D".to_string(), "CD3E".to_string(), "CD3G".to_string()], negative_markers: vec!["CD19".to_string(), "CD14".to_string()], }, MarkerGeneSet { cell_type: "B cell".to_string(), positive_markers: vec!["CD19".to_string(), "MS4A1".to_string(), "CD79A".to_string()], negative_markers: vec!["CD3D".to_string(), "CD14".to_string()], }, MarkerGeneSet { cell_type: "Monocyte".to_string(), positive_markers: vec!["CD14".to_string(), "LYZ".to_string(), "CST3".to_string()], negative_markers: vec!["CD3D".to_string(), "CD19".to_string()], }, MarkerGeneSet { cell_type: "NK cell".to_string(), positive_markers: vec!["GNLY".to_string(), "NKG7".to_string(), "KLRD1".to_string()], negative_markers: vec!["CD3D".to_string(), "CD19".to_string()], }, MarkerGeneSet { cell_type: "Dendritic cell".to_string(), positive_markers: vec!["FCER1A".to_string(), "CD1C".to_string()], negative_markers: vec!["CD3D".to_string(), "CD14".to_string()], }, ] } // ============================================================================ // Tests // ============================================================================ #[cfg(test)] mod tests { use super::*; #[test] fn test_sparse_expression() { let expr = SparseExpression { gene_indices: vec![0, 5, 10], values: vec![1.0, 2.5, 0.5], num_genes: 100, }; assert_eq!(expr.get(0), 1.0); assert_eq!(expr.get(5), 2.5); assert_eq!(expr.get(1), 0.0); // Not expressed assert_eq!(expr.num_expressed(), 3); assert!((expr.total_counts() - 4.0).abs() < 0.001); } #[test] fn test_analysis_config_default() { let config = AnalysisConfig::default(); assert_eq!(config.preprocessing.min_genes, 200); assert_eq!(config.dim_reduction.n_pcs, 50); assert_eq!(config.clustering.algorithm, ClusteringAlgorithm::Leiden); } #[test] fn test_sample_datasets() { let datasets = get_sample_datasets(); assert_eq!(datasets.len(), 4); assert!(datasets.iter().any(|d| d.name == "PBMC 3k")); } #[test] fn test_common_markers() { let markers = get_common_markers(); assert!(!markers.is_empty()); let t_cell = markers.iter().find(|m| m.cell_type == "T cell").unwrap(); assert!(t_cell.positive_markers.contains(&"CD3D".to_string())); } #[test] fn test_modality_serialization() { let modality = DataModality::SpatialTranscriptomics; let json = serde_json::to_string(&modality).unwrap(); assert_eq!(json, "\"spatial_transcriptomics\""); } #[test] fn test_cell_cycle_phase() { let phase = CellCyclePhase::S; let json = serde_json::to_string(&phase).unwrap(); assert_eq!(json, "\"S\""); } #[test] fn test_embedding_method() { let method = EmbeddingMethod::Umap; let json = serde_json::to_string(&method).unwrap(); assert_eq!(json, "\"umap\""); } #[test] fn test_quality_metrics() { let qc = QualityMetrics { n_genes: 2000, total_counts: 5000.0, pct_mito: 5.0, pct_ribo: 10.0, doublet_score: Some(0.1), }; assert_eq!(qc.n_genes, 2000); assert!(qc.doublet_score.is_some()); } }