1use crate::error::Converter;
2use crate::Error;
3use crate::RecordDataType;
4use crate::RecordValue;
5use crate::Result;
6use roxmltree::Node;
7
8fn extract_limit(bounds: &Node, tag_name: &str) -> Result<Option<RecordValue>> {
9 if let Some(tag) = bounds.descendants().find(|n| n.has_tag_name(tag_name)) {
10 let type_str = tag
11 .attribute("type")
12 .invalid_err(format!("Cannot find type attribute of limit '{tag_name}'"))?;
13 let value_str = tag.text().unwrap_or("0");
14 Ok(match type_str {
15 "Integer" => Some(RecordValue::Integer(
16 value_str
17 .parse::<i64>()
18 .invalid_err("Cannot parse integer limit value")?,
19 )),
20 "ScaledInteger" => Some(RecordValue::ScaledInteger(
21 value_str
22 .parse::<i64>()
23 .invalid_err("Cannot parse scaled integer limit value")?,
24 )),
25 "Float" => {
26 let single = tag.attribute("precision").unwrap_or("double") == "single";
27 if single {
28 Some(RecordValue::Single(
29 value_str
30 .parse::<f32>()
31 .invalid_err("Cannot parse single limit value")?,
32 ))
33 } else {
34 Some(RecordValue::Double(
35 value_str
36 .parse::<f64>()
37 .invalid_err("Cannot parse double limit value")?,
38 ))
39 }
40 }
41 _ => Error::not_implemented(format!(
42 "Found unsupported limit of type '{type_str}' for '{tag_name}'"
43 ))?,
44 })
45 } else {
46 Ok(None)
47 }
48}
49
50#[derive(Clone, Debug)]
52pub struct IntensityLimits {
53 pub intensity_min: Option<RecordValue>,
54 pub intensity_max: Option<RecordValue>,
55}
56
57impl IntensityLimits {
58 pub(crate) fn from_node(node: &Node) -> Result<Self> {
59 let intensity_min = extract_limit(node, "intensityMinimum")?;
60 let intensity_max = extract_limit(node, "intensityMaximum")?;
61 Ok(Self {
62 intensity_min,
63 intensity_max,
64 })
65 }
66
67 pub(crate) fn from_record_type(data_type: &RecordDataType) -> Self {
68 let (intensity_min, intensity_max) = data_type.limits();
69 Self {
70 intensity_min,
71 intensity_max,
72 }
73 }
74
75 pub(crate) fn xml_string(&self) -> String {
76 let mut xml = String::from("<intensityLimits type=\"Structure\">\n");
77 if let Some(min) = &self.intensity_min {
78 xml += &record_value_to_xml("intensityMinimum", min);
79 }
80 if let Some(max) = &self.intensity_max {
81 xml += &record_value_to_xml("intensityMaximum", max);
82 }
83 xml += "</intensityLimits>\n";
84 xml
85 }
86}
87
88#[derive(Clone, Debug)]
90pub struct ColorLimits {
91 pub red_min: Option<RecordValue>,
92 pub red_max: Option<RecordValue>,
93 pub green_min: Option<RecordValue>,
94 pub green_max: Option<RecordValue>,
95 pub blue_min: Option<RecordValue>,
96 pub blue_max: Option<RecordValue>,
97}
98
99impl ColorLimits {
100 pub(crate) fn from_node(node: &Node) -> Result<Self> {
101 let red_min = extract_limit(node, "colorRedMinimum")?;
102 let red_max = extract_limit(node, "colorRedMaximum")?;
103 let green_min = extract_limit(node, "colorGreenMinimum")?;
104 let green_max = extract_limit(node, "colorGreenMaximum")?;
105 let blue_min = extract_limit(node, "colorBlueMinimum")?;
106 let blue_max = extract_limit(node, "colorBlueMaximum")?;
107 Ok(Self {
108 red_min,
109 red_max,
110 green_min,
111 green_max,
112 blue_min,
113 blue_max,
114 })
115 }
116
117 pub(crate) fn from_record_types(
118 red: &RecordDataType,
119 green: &RecordDataType,
120 blue: &RecordDataType,
121 ) -> Self {
122 let (red_min, red_max) = red.limits();
123 let (green_min, green_max) = green.limits();
124 let (blue_min, blue_max) = blue.limits();
125 Self {
126 red_min,
127 red_max,
128 green_min,
129 green_max,
130 blue_min,
131 blue_max,
132 }
133 }
134
135 pub(crate) fn xml_string(&self) -> String {
136 let mut xml = String::from("<colorLimits type=\"Structure\">\n");
137 if let Some(min) = &self.red_min {
138 xml += &record_value_to_xml("colorRedMinimum", min);
139 }
140 if let Some(max) = &self.red_max {
141 xml += &record_value_to_xml("colorRedMaximum", max);
142 }
143 if let Some(min) = &self.green_min {
144 xml += &record_value_to_xml("colorGreenMinimum", min);
145 }
146 if let Some(max) = &self.green_max {
147 xml += &record_value_to_xml("colorGreenMaximum", max);
148 }
149 if let Some(min) = &self.blue_min {
150 xml += &record_value_to_xml("colorBlueMinimum", min);
151 }
152 if let Some(max) = &self.blue_max {
153 xml += &record_value_to_xml("colorBlueMaximum", max);
154 }
155 xml += "</colorLimits>\n";
156 xml
157 }
158}
159
160fn record_value_to_xml(tag_name: &str, value: &RecordValue) -> String {
162 match value {
163 RecordValue::Integer(value) => {
164 format!("<{tag_name} type=\"Integer\">{value}</{tag_name}>\n")
165 }
166 RecordValue::ScaledInteger(value) => {
167 format!("<{tag_name} type=\"ScaledInteger\">{value}</{tag_name}>\n")
168 }
169 RecordValue::Single(value) => {
170 format!("<{tag_name} type=\"Float\" precision=\"single\">{value}</{tag_name}>\n")
171 }
172 RecordValue::Double(value) => format!("<{tag_name} type=\"Float\">{value}</{tag_name}>\n"),
173 }
174}