1use std::{
4 io::{BufRead, Write},
5 str,
6};
7
8use log::warn;
9use paste::paste;
10use quick_xml::{events::Event, Reader, Writer};
11use serde_json::Value;
12
13use super::{
14 datatype::Datatype,
15 desc::Description,
16 error::VOTableError,
17 field::{ArraySize, Field, Precision},
18 link::Link,
19 utils::{discard_comment, discard_event},
20 values::Values,
21 HasSubElements, HasSubElems, QuickXmlReadWrite, TableDataContent, VOTableElement, VOTableVisitor,
22};
23
24#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
26pub struct Param {
27 #[serde(flatten)]
28 pub field: Field,
29 pub value: String,
30}
31
32impl Param {
33 pub fn new<N: Into<String>, V: Into<String>>(name: N, datatype: Datatype, value: V) -> Self {
34 Param {
35 field: Field::new(name, datatype),
36 value: value.into(),
37 }
38 }
39
40 impl_builder_opt_string_attr_delegated!(id, field);
42 impl_builder_mandatory_string_attr_delegated!(name, field);
43 impl_builder_mandatory_attr_delegated!(datatype, Datatype, field);
44 impl_builder_opt_string_attr_delegated!(unit, field);
45 impl_builder_opt_attr_delegated!(precision, Precision, field);
46 impl_builder_opt_attr_delegated!(width, u16, field);
47 impl_builder_opt_string_attr_delegated!(xtype, field);
48 impl_builder_opt_string_attr_delegated!(ref_, ref, field);
49 impl_builder_opt_string_attr_delegated!(ucd, field);
50 impl_builder_opt_string_attr_delegated!(utype, field);
51 impl_builder_opt_attr_delegated!(arraysize, ArraySize, field);
52 impl_builder_mandatory_string_attr!(value);
53 impl_builder_insert_extra_delegated!(field);
55 impl_builder_opt_subelem_delegated!(description, Description, field);
57 impl_builder_opt_subelem_delegated!(values, Values, field);
58 impl_builder_push_delegated!(Link, field);
59
60 pub fn visit<C, V>(&mut self, visitor: &mut V) -> Result<(), V::E>
61 where
62 C: TableDataContent,
63 V: VOTableVisitor<C>,
64 {
65 visitor.visit_param_start(self)?;
66 if let Some(description) = &mut self.field.description {
67 visitor.visit_description(description)?;
68 }
69 if let Some(values) = &mut self.field.values {
70 values.visit(visitor)?;
71 }
72 for l in &mut self.field.links {
73 visitor.visit_link(l)?;
74 }
75 visitor.visit_param_ended(self)
76 }
77}
78
79impl VOTableElement for Param {
80 const TAG: &'static str = "PARAM";
81
82 type MarkerType = HasSubElems;
83
84 fn from_attrs<K, V, I>(attrs: I) -> Result<Self, VOTableError>
85 where
86 K: AsRef<str> + Into<String>,
87 V: AsRef<str> + Into<String>,
88 I: Iterator<Item = (K, V)>,
89 {
90 const DEFAULT_VALUE: &str = "@TBD";
91 const DEFAULT_DT: Datatype = Datatype::Logical;
92 let mut name_found = false;
93 let mut dt_found = false;
94 let mut val_found = false;
95 Self::new(DEFAULT_VALUE, DEFAULT_DT, DEFAULT_VALUE)
96 .set_attrs(attrs.map(|(k, v)| {
97 match k.as_ref() {
98 "name" => name_found = true,
99 "datatype" => dt_found = true,
100 "value" => val_found = true,
101 _ => {}
102 };
103 (k, v)
104 }))
105 .and_then(|param| {
106 if name_found && dt_found && val_found {
107 Ok(param)
108 } else {
109 Err(VOTableError::Custom(format!(
110 "Attributes 'name', 'datatype' and 'value' are mandatory in tag '{}'",
111 Self::TAG
112 )))
113 }
114 })
115 }
116
117 fn set_attrs_by_ref<K, V, I>(&mut self, attrs: I) -> Result<(), VOTableError>
118 where
119 K: AsRef<str> + Into<String>,
120 V: AsRef<str> + Into<String>,
121 I: Iterator<Item = (K, V)>,
122 {
123 for (key, val) in attrs {
124 let key = key.as_ref();
125 match key {
126 "ID" => self.set_id_by_ref(val),
127 "name" => self.set_name_by_ref(val),
128 "datatype" => {
129 self.set_datatype_by_ref(val.as_ref().parse().map_err(VOTableError::ParseDatatype)?)
130 }
131 "unit" => self.set_unit_by_ref(val),
132 "precision" => {
133 if val.as_ref().is_empty() {
134 warn!(
135 "Emtpy 'precision' attribute in tag {}: attribute ignored",
136 Self::TAG
137 )
138 } else {
139 self.set_precision_by_ref(val.as_ref().parse().map_err(VOTableError::ParseInt)?)
140 }
141 }
142 "width" => self.set_width_by_ref(val.as_ref().parse().map_err(VOTableError::ParseInt)?),
143 "xtype" => self.set_xtype_by_ref(val),
144 "ref" => self.set_ref_by_ref(val),
145 "ucd" => self.set_ucd_by_ref(val),
146 "utype" => self.set_utype_by_ref(val),
147 "arraysize" => {
148 self.set_arraysize_by_ref(val.as_ref().parse().map_err(VOTableError::ParseInt)?)
149 }
150 "value" => self.set_value_by_ref(val),
151 _ => self.insert_extra_str_by_ref(key, val),
152 }
153 }
154 Ok(())
155 }
156
157 fn for_each_attribute<F>(&self, mut f: F)
158 where
159 F: FnMut(&str, &str),
160 {
161 if let Some(id) = &self.field.id {
162 f("ID", id.as_str());
163 }
164 f("name", self.field.name.as_str());
165 f("datatype", self.field.datatype.to_string().as_str());
166 f("value", self.value.as_str());
167 if let Some(arraysize) = &self.field.arraysize {
168 f("arraysize", arraysize.to_string().as_str());
169 }
170 if let Some(width) = &self.field.width {
171 f("width", width.to_string().as_str());
172 }
173 if let Some(precision) = &self.field.precision {
174 f("precision", precision.to_string().as_str());
175 }
176 if let Some(unit) = &self.field.unit {
177 f("unit", unit.as_str());
178 }
179 if let Some(ucd) = &self.field.ucd {
180 f("ucd", ucd.as_str());
181 }
182 if let Some(utype) = &self.field.utype {
183 f("utype", utype.as_str());
184 }
185 if let Some(xtype) = &self.field.xtype {
186 f("xtype", xtype.as_str());
187 }
188 if let Some(ref_) = &self.field.ref_ {
189 f("ref", ref_.as_str());
190 }
191 for_each_extra_attribute_delegated!(self, field, f);
192 }
193}
194
195impl HasSubElements for Param {
196 type Context = ();
197
198 fn has_no_sub_elements(&self) -> bool {
199 self.field.has_no_sub_elements()
200 }
201
202 fn read_sub_elements_by_ref<R: BufRead>(
203 &mut self,
204 mut reader: &mut Reader<R>,
205 mut reader_buff: &mut Vec<u8>,
206 _context: &Self::Context,
207 ) -> Result<(), VOTableError> {
208 loop {
209 let mut event = reader.read_event(reader_buff).map_err(VOTableError::Read)?;
210 match &mut event {
211 Event::Start(ref e) => match e.local_name() {
212 Description::TAG_BYTES => {
213 set_from_event_start!(self, Description, reader, reader_buff, e)
214 }
215 Values::TAG_BYTES => set_from_event_start!(self, Values, reader, reader_buff, e),
216 Link::TAG_BYTES => push_from_event_start!(self, Link, reader, reader_buff, e),
217 _ => {
218 return Err(VOTableError::UnexpectedStartTag(
219 e.local_name().to_vec(),
220 Self::TAG,
221 ))
222 }
223 },
224 Event::Empty(ref e) => match e.local_name() {
225 Values::TAG_BYTES => set_from_event_empty!(self, Values, e),
226 Link::TAG_BYTES => push_from_event_empty!(self, Link, e),
227 _ => {
228 return Err(VOTableError::UnexpectedEmptyTag(
229 e.local_name().to_vec(),
230 Self::TAG,
231 ))
232 }
233 },
234 Event::End(e) if e.local_name() == Self::TAG_BYTES => return Ok(()),
235 Event::Eof => return Err(VOTableError::PrematureEOF(Self::TAG)),
236 Event::Comment(e) => discard_comment(e, reader, Self::TAG),
237 _ => discard_event(event, Self::TAG),
238 }
239 }
240 }
241
242 fn write_sub_elements_by_ref<W: Write>(
243 &mut self,
244 writer: &mut Writer<W>,
245 context: &Self::Context,
246 ) -> Result<(), VOTableError> {
247 self.field.write_sub_elements_by_ref(writer, context)
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use crate::{
254 param::Param,
255 tests::{test_read, test_writer},
256 };
257
258 #[test]
259 fn test_params_read_write() {
260 let xml = r#"<PARAM name="Freq" datatype="float" value="352" ucd="em.freq" utype="MHz"/>"#; let param = test_read::<Param>(xml);
262 assert_eq!(param.value.as_str(), "352");
264 test_writer(param, xml)
266 }
267}