use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct QmriMetadata {
#[serde(default)]
pub repetition_time: Option<f64>,
#[serde(default)]
pub echo_time: Option<serde_json::Value>,
#[serde(default)]
pub flip_angle: Option<serde_json::Value>,
#[serde(default)]
pub inversion_time: Option<f64>,
#[serde(rename = "MTState", default)]
pub mt_state: Option<String>,
#[serde(rename = "MTPulseBandwidth", default)]
pub mt_pulse_bandwidth: Option<f64>,
#[serde(rename = "MTPulseDuration", default)]
pub mt_pulse_duration: Option<f64>,
#[serde(rename = "MTPulseShape", default)]
pub mt_pulse_shape: Option<String>,
#[serde(default)]
pub number_of_echoes: Option<u32>,
#[serde(default)]
pub number_of_flip_angles: Option<u32>,
#[serde(rename = "InversionTime1", default)]
pub inversion_time_1: Option<f64>,
#[serde(rename = "InversionTime2", default)]
pub inversion_time_2: Option<f64>,
#[serde(rename = "NumberShots", default)]
pub number_shots: Option<u32>,
#[serde(rename = "B1MappingMethod", default)]
pub b1_mapping_method: Option<String>,
#[serde(default)]
pub units: Option<String>,
#[serde(default)]
pub fitting_method: Option<String>,
#[serde(rename = "B1Corrected", default)]
pub b1_corrected: Option<bool>,
#[serde(default)]
pub manufacturer: Option<String>,
#[serde(default)]
pub magnetic_field_strength: Option<f64>,
#[serde(default)]
pub pulse_sequence_type: Option<String>,
}
impl QmriMetadata {
pub fn from_metadata(md: &bids_core::metadata::BidsMetadata) -> Option<Self> {
md.deserialize_as()
}
#[must_use]
pub fn echo_times(&self) -> Vec<f64> {
match &self.echo_time {
Some(serde_json::Value::Number(n)) => n.as_f64().map_or_else(Vec::new, |v| vec![v]),
Some(serde_json::Value::Array(arr)) => arr.iter().filter_map(|v| v.as_f64()).collect(),
_ => Vec::new(),
}
}
#[must_use]
pub fn flip_angles(&self) -> Vec<f64> {
match &self.flip_angle {
Some(serde_json::Value::Number(n)) => n.as_f64().map_or_else(Vec::new, |v| vec![v]),
Some(serde_json::Value::Array(arr)) => arr.iter().filter_map(|v| v.as_f64()).collect(),
_ => Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_qmri_metadata_parse() {
let json = r#"{
"RepetitionTime": 0.025,
"EchoTime": [0.002, 0.006, 0.010],
"FlipAngle": 6,
"MTState": "on",
"MagneticFieldStrength": 3.0
}"#;
let md: QmriMetadata = serde_json::from_str(json).unwrap();
assert_eq!(md.repetition_time, Some(0.025));
assert_eq!(md.echo_times(), vec![0.002, 0.006, 0.010]);
assert_eq!(md.flip_angles(), vec![6.0]);
assert_eq!(md.mt_state.as_deref(), Some("on"));
assert_eq!(md.magnetic_field_strength, Some(3.0));
}
#[test]
fn test_qmri_mp2rage() {
let json = r#"{
"InversionTime1": 0.7,
"InversionTime2": 2.5,
"FlipAngle": [4, 5],
"NumberShots": 176
}"#;
let md: QmriMetadata = serde_json::from_str(json).unwrap();
assert_eq!(md.inversion_time_1, Some(0.7));
assert_eq!(md.inversion_time_2, Some(2.5));
assert_eq!(md.flip_angles(), vec![4.0, 5.0]);
assert_eq!(md.number_shots, Some(176));
}
}