1use serde::{Deserialize, Serialize};
18use std::path::Path;
19
20use crate::error::Result;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "PascalCase")]
28pub struct GeneticInfo {
29 #[serde(default)]
31 pub dataset: Option<String>,
32 #[serde(default)]
34 pub genetics: Option<GeneticsField>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "PascalCase")]
40pub struct GeneticsField {
41 #[serde(default)]
43 pub database: Option<String>,
44 #[serde(default)]
46 pub descriptors: Option<serde_json::Value>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct GeneticDatabase {
55 #[serde(flatten)]
57 pub entries: std::collections::HashMap<String, serde_json::Value>,
58}
59
60impl GeneticInfo {
61 pub fn from_dir(dir: &Path) -> Result<Option<Self>> {
69 let path = dir.join("genetic_info.json");
70 if !path.exists() {
71 return Ok(None);
72 }
73 let contents = std::fs::read_to_string(&path)?;
74 let info: Self = serde_json::from_str(&contents)?;
75 Ok(Some(info))
76 }
77}
78
79impl GeneticDatabase {
80 pub fn from_dir(dir: &Path) -> Result<Option<Self>> {
88 let path = dir.join("genetic_database.json");
89 if !path.exists() {
90 return Ok(None);
91 }
92 let contents = std::fs::read_to_string(&path)?;
93 let db: Self = serde_json::from_str(&contents)?;
94 Ok(Some(db))
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_genetic_info_parse() {
104 let json = r#"{
105 "Dataset": "Example",
106 "Genetics": {
107 "Database": "https://www.ncbi.nlm.nih.gov/gap/",
108 "Descriptors": ["APOE", "BDNF"]
109 }
110 }"#;
111 let info: GeneticInfo = serde_json::from_str(json).unwrap();
112 assert_eq!(info.dataset.as_deref(), Some("Example"));
113 let genetics = info.genetics.unwrap();
114 assert!(genetics.database.as_ref().unwrap().contains("ncbi"));
115 }
116
117 #[test]
118 fn test_genetic_database_parse() {
119 let json = r#"{
120 "sub-01": {"sample_id": "GENO_001"},
121 "sub-02": {"sample_id": "GENO_002"}
122 }"#;
123 let db: GeneticDatabase = serde_json::from_str(json).unwrap();
124 assert_eq!(db.entries.len(), 2);
125 assert!(db.entries.contains_key("sub-01"));
126 }
127
128 #[test]
129 fn test_genetic_info_missing_file() {
130 let dir = std::env::temp_dir().join("bids_genetic_test_missing");
131 std::fs::create_dir_all(&dir).unwrap();
132 let result = GeneticInfo::from_dir(&dir).unwrap();
133 assert!(result.is_none());
134 std::fs::remove_dir_all(&dir).unwrap();
135 }
136}