1extern crate core;
2
3use std::{
18 error::Error,
19 io::{BufRead, Write},
20 str::from_utf8,
21};
22
23use quick_xml::{
24 events::{attributes::Attributes, BytesStart, BytesText, Event},
25 Reader, Writer,
26};
27
28#[macro_use]
29mod macros;
30mod utils;
31
32pub mod coosys;
33pub mod data;
34pub mod datatype;
35pub mod definitions;
36pub mod desc;
37pub mod error;
38pub mod field;
39pub mod fieldref;
40pub mod group;
41pub mod impls;
42pub mod info;
43pub mod iter;
44pub mod link;
45pub mod param;
46pub mod paramref;
47pub mod resource;
48pub mod table;
49pub mod timesys;
50pub mod values;
51pub mod votable;
52
53#[cfg(feature = "mivot")]
54pub mod mivot;
55
56#[cfg(feature = "mivot")]
57pub use self::mivot::VodmlVisitor;
58use self::utils::{discard_comment, discard_event};
59pub use self::{
60 coosys::CooSys,
61 data::{
62 binary::Binary, binary2::Binary2, fits::Fits, stream::Stream, tabledata::TableData, Data,
63 },
64 definitions::Definitions,
65 desc::Description,
66 error::VOTableError,
67 field::Field,
68 fieldref::FieldRef,
69 group::{Group, TableGroup},
70 impls::mem::VoidTableDataContent,
71 info::Info,
72 link::Link,
73 param::Param,
74 paramref::ParamRef,
75 resource::Resource,
76 table::Table,
77 table::TableElem,
78 timesys::TimeSys,
79 values::{Max, Min, Opt, Values},
80 votable::VOTable,
81};
82
83pub trait VOTableElement: Sized {
84 const TAG: &'static str;
86 const TAG_BYTES: &'static [u8] = Self::TAG.as_bytes();
87
88 type MarkerType: VOTableElementType;
89
90 fn tag(&self) -> &'static str {
92 Self::TAG
93 }
94
95 fn tag_bytes(&self) -> &'static [u8] {
96 Self::TAG_BYTES
97 }
98
99 fn from_attrs<K, V, I>(attrs: I) -> Result<Self, VOTableError>
109 where
110 K: AsRef<str> + Into<String>,
111 V: AsRef<str> + Into<String>,
112 I: Iterator<Item = (K, V)>;
113
114 fn set_attrs<K, V, I>(mut self, attrs: I) -> Result<Self, VOTableError>
116 where
117 K: AsRef<str> + Into<String>,
118 V: AsRef<str> + Into<String>,
119 I: Iterator<Item = (K, V)>,
120 {
121 self.set_attrs_by_ref(attrs).map(|_| self)
122 }
123
124 fn set_attrs_by_ref<K, V, I>(&mut self, attrs: I) -> Result<(), VOTableError>
126 where
127 K: AsRef<str> + Into<String>,
128 V: AsRef<str> + Into<String>,
129 I: Iterator<Item = (K, V)>;
130
131 fn for_each_attribute<F>(&self, f: F)
133 where
134 F: FnMut(&str, &str);
135
136 fn get_attrs(&self) -> Vec<(String, String)> {
137 let mut attrs = Vec::new();
138 self.for_each_attribute(|k, v| attrs.push((k.to_string(), v.to_string())));
139 attrs
140 }
141}
142
143pub trait VOTableElementType {}
152pub struct EmptyElem;
154impl VOTableElementType for EmptyElem {}
155pub struct HasContentElem;
157impl VOTableElementType for HasContentElem {}
158pub struct HasSubElems;
160impl VOTableElementType for HasSubElems {}
161pub struct SpecialElem;
163impl VOTableElementType for SpecialElem {}
164
165pub trait IsEmpty: VOTableElement<MarkerType = EmptyElem> {}
167impl<T> IsEmpty for T where T: VOTableElement<MarkerType = EmptyElem> {}
169
170impl<T: IsEmpty> QuickXmlReadWrite<EmptyElem> for T {
171 type Context = ();
172
173 fn read_content_by_ref<R: BufRead>(
174 &mut self,
175 _reader: &mut Reader<R>,
176 _reader_buff: &mut Vec<u8>,
177 _context: &Self::Context,
178 ) -> Result<(), VOTableError> {
179 Ok(())
180 }
181
182 fn write<W: Write>(
183 &mut self,
184 writer: &mut Writer<W>,
185 _context: &Self::Context,
186 ) -> Result<(), VOTableError> {
187 let mut elem_writer = writer.create_element(Self::TAG_BYTES);
188 elem_writer = elem_writer.with_attributes(
189 self
190 .get_attrs()
191 .iter()
192 .map(|(k, v)| (k.as_str(), v.as_str())),
193 );
194 elem_writer
195 .write_empty()
196 .map_err(VOTableError::Write)
197 .map(|_| ())
198 }
199}
200
201pub trait HasContent: VOTableElement<MarkerType = HasContentElem> {
204 fn get_content(&self) -> Option<&str>;
206 fn set_content<S: Into<String>>(self, content: S) -> Self;
208 fn set_content_by_ref<S: Into<String>>(&mut self, content: S);
210}
211impl<T: HasContent> QuickXmlReadWrite<HasContentElem> for T {
212 type Context = ();
213
214 fn read_content_by_ref<R: BufRead>(
215 &mut self,
216 reader: &mut Reader<R>,
217 reader_buff: &mut Vec<u8>,
218 _context: &Self::Context,
219 ) -> Result<(), VOTableError> {
220 let mut content = String::new();
221 loop {
222 let mut event = reader.read_event(reader_buff).map_err(VOTableError::Read)?;
223 match &mut event {
224 Event::Text(e) => content.push_str(
225 e.unescape_and_decode(reader)
226 .map_err(VOTableError::Read)?
227 .as_str(),
228 ),
229 Event::CData(e) => {
230 content.push_str(from_utf8(e.clone().into_inner().as_ref()).map_err(VOTableError::Utf8)?)
231 }
232 Event::End(e) if e.local_name() == Self::TAG_BYTES => {
233 self.set_content_by_ref(content);
234 reader_buff.clear();
235 return Ok(());
236 }
237 Event::Eof => return Err(VOTableError::PrematureEOF(Self::TAG)),
238 Event::Comment(e) => discard_comment(e, reader, Self::TAG),
239 _ => discard_event(event, Self::TAG),
240 }
241 }
242 }
243
244 fn write<W: Write>(
245 &mut self,
246 writer: &mut Writer<W>,
247 _context: &Self::Context,
248 ) -> Result<(), VOTableError> {
249 let mut elem_writer = writer.create_element(Self::TAG_BYTES);
250 elem_writer = elem_writer.with_attributes(
251 self
252 .get_attrs()
253 .iter()
254 .map(|(k, v)| (k.as_str(), v.as_str())),
255 );
256 if let Some(content) = self.get_content() {
257 elem_writer.write_text_content(BytesText::from_plain_str(content))
258 } else {
259 elem_writer.write_empty()
260 }
261 .map_err(VOTableError::Write)
262 .map(|_| ())
263 }
264}
265
266pub trait HasSubElements: VOTableElement<MarkerType = HasSubElems> {
268 type Context;
269
270 fn has_no_sub_elements(&self) -> bool;
272
273 fn read_sub_elements_and_clean<R: BufRead>(
275 &mut self,
276 reader: Reader<R>,
277 reader_buff: &mut Vec<u8>,
278 context: &Self::Context,
279 ) -> Result<Reader<R>, VOTableError> {
280 let res = self.read_sub_elements(reader, reader_buff, context);
281 reader_buff.clear();
282 res
283 }
284
285 fn read_sub_elements<R: BufRead>(
288 &mut self,
289 mut reader: Reader<R>,
290 reader_buff: &mut Vec<u8>,
291 context: &Self::Context,
292 ) -> Result<Reader<R>, VOTableError> {
293 self
294 .read_sub_elements_by_ref(&mut reader, reader_buff, context)
295 .map(|()| reader)
296 }
297
298 fn read_sub_elements_and_clean_by_ref<R: BufRead>(
300 &mut self,
301 reader: &mut Reader<R>,
302 reader_buff: &mut Vec<u8>,
303 context: &Self::Context,
304 ) -> Result<(), VOTableError> {
305 let res = self.read_sub_elements_by_ref(reader, reader_buff, context);
306 reader_buff.clear();
307 res
308 }
309
310 fn read_sub_elements_by_ref<R: BufRead>(
313 &mut self,
314 reader: &mut Reader<R>,
315 reader_buff: &mut Vec<u8>,
316 context: &Self::Context,
317 ) -> Result<(), VOTableError>;
318
319 fn write_sub_elements_by_ref<W: Write>(
321 &mut self,
322 writer: &mut Writer<W>,
323 context: &Self::Context,
324 ) -> Result<(), VOTableError>;
325}
326
327impl<T: HasSubElements> QuickXmlReadWrite<HasSubElems> for T {
328 type Context = <Self as HasSubElements>::Context;
329
330 fn read_content_by_ref<R: BufRead>(
331 &mut self,
332 reader: &mut Reader<R>,
333 reader_buff: &mut Vec<u8>,
334 context: &Self::Context,
335 ) -> Result<(), VOTableError> {
336 self.read_sub_elements_by_ref(reader, reader_buff, context)
337 }
338
339 fn write<W: Write>(
343 &mut self,
344 writer: &mut Writer<W>,
345 context: &Self::Context,
346 ) -> Result<(), VOTableError> {
347 if self.has_no_sub_elements() {
348 let mut elem_writer = writer.create_element(Self::TAG_BYTES);
349 elem_writer = elem_writer.with_attributes(
350 self
351 .get_attrs()
352 .iter()
353 .map(|(k, v)| (k.as_str(), v.as_str())),
354 );
355 elem_writer
356 .write_empty()
357 .map_err(VOTableError::Write)
358 .map(|_| ())
359 } else {
360 let mut tag = BytesStart::borrowed_name(Self::TAG_BYTES);
361 self.for_each_attribute(|k, v| tag.push_attribute((k, v)));
363 writer
364 .write_event(Event::Start(tag.to_borrowed()))
365 .map_err(VOTableError::Write)?;
366 self.write_sub_elements_by_ref(writer, context)?;
368 writer
370 .write_event(Event::End(tag.to_end()))
371 .map_err(VOTableError::Write)
372 }
373 }
374}
375
376pub trait TableDataContent: Default + PartialEq + serde::Serialize {
377 fn new() -> Self {
378 Self::default()
379 }
380
381 fn ensures_consistency(&mut self, context: &[TableElem]) -> Result<(), String>;
397
398 fn read_datatable_content<R: BufRead>(
401 &mut self,
402 reader: &mut Reader<R>,
403 reader_buff: &mut Vec<u8>,
404 context: &[TableElem],
405 ) -> Result<(), VOTableError>;
406
407 fn read_binary_content<R: BufRead>(
410 &mut self,
411 reader: &mut Reader<R>,
412 reader_buff: &mut Vec<u8>,
413 context: &[TableElem],
414 ) -> Result<(), VOTableError>;
415
416 fn read_binary2_content<R: BufRead>(
419 &mut self,
420 reader: &mut Reader<R>,
421 reader_buff: &mut Vec<u8>,
422 context: &[TableElem],
423 ) -> Result<(), VOTableError>;
424
425 fn write_in_datatable<W: Write>(
426 &mut self,
427 writer: &mut Writer<W>,
428 context: &[TableElem],
429 ) -> Result<(), VOTableError>;
430
431 fn write_in_binary<W: Write>(
432 &mut self,
433 writer: &mut Writer<W>,
434 context: &[TableElem],
435 ) -> Result<(), VOTableError>;
436
437 fn write_in_binary2<W: Write>(
438 &mut self,
439 writer: &mut Writer<W>,
440 context: &[TableElem],
441 ) -> Result<(), VOTableError>;
442}
443
444pub trait QuickXmlReadWrite<VOTableElementType>: VOTableElement {
448 type Context;
449
450 fn from_event_empty(e: &BytesStart) -> Result<Self, VOTableError> {
451 Self::from_event_start(e)
452 }
453
454 fn from_event_start(e: &BytesStart) -> Result<Self, VOTableError> {
455 Self::from_attributes(e.attributes())
456 }
457
458 fn from_attributes(attrs: Attributes) -> Result<Self, VOTableError> {
460 Self::quick_xml_attrs_to_vec(attrs).and_then(|attrs| Self::from_attrs(attrs.into_iter()))
461 }
462
463 fn quick_xml_attrs_to_vec(attrs: Attributes) -> Result<Vec<(String, String)>, VOTableError> {
479 attrs
480 .map(|r| {
482 r.map_err(VOTableError::Attr).and_then(|attr| {
483 from_utf8(attr.key)
484 .map_err(VOTableError::Utf8)
485 .and_then(|key| {
486 attr
487 .unescaped_value()
488 .map_err(VOTableError::Read)
489 .and_then(|val| {
490 from_utf8(val.as_ref())
491 .map(|val| val.to_string())
492 .map_err(VOTableError::Utf8)
493 })
494 .map(
495 |val| (key.to_string(), val),
496 )
504 })
505 })
506 })
508 .collect::<Result<Vec<(String, String)>, _>>()
509 }
510
511 fn read_content<R: BufRead>(
512 mut self,
513 reader: &mut Reader<R>,
514 reader_buff: &mut Vec<u8>,
515 context: &Self::Context,
516 ) -> Result<Self, VOTableError> {
517 self
518 .read_content_by_ref(reader, reader_buff, context)
519 .map(|()| self)
520 }
521
522 fn read_content_by_ref<R: BufRead>(
523 &mut self,
524 reader: &mut Reader<R>,
525 reader_buff: &mut Vec<u8>,
526 context: &Self::Context,
527 ) -> Result<(), VOTableError>;
528
529 fn write<W: Write>(
533 &mut self,
534 writer: &mut Writer<W>,
535 context: &Self::Context,
536 ) -> Result<(), VOTableError>;
537}
538
539pub trait VOTableVisitor<C: TableDataContent> {
543 type E: Error;
544
545 #[cfg(feature = "mivot")]
546 type M: VodmlVisitor<E = Self::E>;
547
548 fn visit_votable_start(&mut self, votable: &mut VOTable<C>) -> Result<(), Self::E>;
549 fn visit_votable_ended(&mut self, votable: &mut VOTable<C>) -> Result<(), Self::E>;
550
551 fn visit_description(&mut self, description: &mut Description) -> Result<(), Self::E>; fn visit_coosys_start(&mut self, coosys: &mut CooSys) -> Result<(), Self::E>;
553 fn visit_coosys_ended(&mut self, coosys: &mut CooSys) -> Result<(), Self::E>;
554 fn visit_timesys(&mut self, timesys: &mut TimeSys) -> Result<(), Self::E>; fn visit_group_start(&mut self, group: &mut Group) -> Result<(), Self::E>;
556 fn visit_group_ended(&mut self, group: &mut Group) -> Result<(), Self::E>;
557
558 #[cfg(feature = "mivot")]
559 fn get_mivot_visitor(&mut self) -> Self::M;
560
561 fn visit_table_group_start(&mut self, group: &mut TableGroup) -> Result<(), Self::E>;
562 fn visit_table_group_ended(&mut self, group: &mut TableGroup) -> Result<(), Self::E>;
563
564 fn visit_paramref(&mut self, paramref: &mut ParamRef) -> Result<(), Self::E>; fn visit_fieldref(&mut self, fieldref: &mut FieldRef) -> Result<(), Self::E>; fn visit_param_start(&mut self, param: &mut Param) -> Result<(), Self::E>;
568 fn visit_param_ended(&mut self, param: &mut Param) -> Result<(), Self::E>;
569
570 fn visit_field_start(&mut self, field: &mut Field) -> Result<(), Self::E>;
571 fn visit_field_ended(&mut self, field: &mut Field) -> Result<(), Self::E>;
572
573 fn visit_info(&mut self, info: &mut Info) -> Result<(), Self::E>; fn visit_definitions_start(&mut self, coosys: &mut Definitions) -> Result<(), Self::E>;
575 fn visit_definitions_ended(&mut self, coosys: &mut Definitions) -> Result<(), Self::E>;
576
577 fn visit_resource_start(&mut self, resource: &mut Resource<C>) -> Result<(), Self::E>;
578 fn visit_resource_ended(&mut self, resource: &mut Resource<C>) -> Result<(), Self::E>;
579
580 fn visit_post_info(&mut self, info: &mut Info) -> Result<(), Self::E>;
581
582 fn visit_resource_sub_elem_start(&mut self) -> Result<(), Self::E>;
584 fn visit_resource_sub_elem_ended(&mut self) -> Result<(), Self::E>;
585
586 fn visit_link(&mut self, link: &mut Link) -> Result<(), Self::E>; fn visit_table_start(&mut self, table: &mut Table<C>) -> Result<(), Self::E>;
589 fn visit_table_ended(&mut self, table: &mut Table<C>) -> Result<(), Self::E>;
590
591 fn visit_data_start(&mut self, data: &mut Data<C>) -> Result<(), Self::E>;
592 fn visit_data_ended(&mut self, data: &mut Data<C>) -> Result<(), Self::E>;
593
594 fn visit_tabledata(&mut self, table: &mut TableData<C>) -> Result<(), Self::E>;
595 fn visit_binary_stream(&mut self, stream: &mut Stream<C>) -> Result<(), Self::E>;
596 fn visit_binary2_stream(&mut self, stream: &mut Stream<C>) -> Result<(), Self::E>;
597 fn visit_fits_start(&mut self, fits: &mut Fits) -> Result<(), Self::E>;
598 fn visit_fits_stream(&mut self, stream: &mut Stream<VoidTableDataContent>)
599 -> Result<(), Self::E>;
600 fn visit_fits_ended(&mut self, fits: &mut Fits) -> Result<(), Self::E>;
601
602 fn visit_values_start(&mut self, values: &mut Values) -> Result<(), Self::E>;
603 fn visit_values_min(&mut self, min: &mut Min) -> Result<(), Self::E>; fn visit_values_max(&mut self, max: &mut Max) -> Result<(), Self::E>; fn visit_values_opt_start(&mut self, opt: &mut Opt) -> Result<(), Self::E>;
606 fn visit_values_opt_ended(&mut self, opt: &mut Opt) -> Result<(), Self::E>;
607 fn visit_values_ended(&mut self, values: &mut Values) -> Result<(), Self::E>;
608}
609
610#[cfg(test)]
611mod tests {
612 use std::{i64, io::Cursor, str::from_utf8};
613
614 use quick_xml::{events::Event, Reader, Writer};
615 use serde_json::{Number, Value};
616
617 use super::{
618 coosys::{CooSys, System},
619 data::Data,
620 datatype::Datatype,
621 field::{ArraySize, Field, Precision},
622 impls::{mem::InMemTableDataRows, VOTableValue},
623 info::Info,
624 link::Link,
625 resource::{Resource, ResourceSubElem},
626 table::Table,
627 values::Values,
628 votable::{VOTable, Version},
629 HasContent, QuickXmlReadWrite, VOTableElement,
630 };
631
632 #[test]
633 fn test_create_in_mem_1() {
634 let rows = vec![
635 vec![
636 VOTableValue::Null, VOTableValue::CharASCII('*'),
638 VOTableValue::Long(i64::max_value()),
639 ],
640 vec![
641 VOTableValue::Double(0.4581e+38),
642 VOTableValue::Null,
643 VOTableValue::Long(i64::min_value()),
644 ],
645 vec![
646 VOTableValue::Null,
647 VOTableValue::CharASCII('*'),
648 VOTableValue::Long(0),
649 ],
650 ];
651 let data_content = InMemTableDataRows::new(rows);
652
653 let table = Table::new()
654 .set_id("V_147_sdss12")
655 .set_name("V/147/sdss12")
656 .set_description("* output of the SDSS photometric catalog".into())
657 .push_field(
658 Field::new("RA_ICRS", Datatype::Double)
659 .set_unit("deg")
660 .set_ucd("pos.eq.ra;meta.main")
661 .set_ref("H")
662 .set_width(10)
663 .set_precision(Precision::new_dec(6))
664 .set_description("Right Ascension of the object (ICRS) (ra)".into())
665 .insert_extra("toto", Number::from_f64(0.5).map(Value::Number).unwrap_or(Value::Null))
666 ).push_field(
667 Field::new("m_SDSS12", Datatype::CharASCII)
668 .set_ucd("meta.code.multip")
669 .set_width(1)
670 .set_description("[*] The asterisk indicates that 2 different SDSS objects share the same SDSS12 name".into())
671 .push_link(Link::new().set_href("http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}"))
672 ).push_field(
673 Field::new("umag", Datatype::LongInt)
674 .set_unit("mag")
675 .set_ucd("phot.mag;em.opt.U")
676 .set_description("[4/38]? Model magnitude in u filter, AB scale (u) (5)".into())
677 .set_values(Values::new().set_null("NaN"))
678 ).set_data(Data::new_empty().set_tabledata(data_content));
679
680 let resource = Resource::default()
681 .set_id("yCat_17011219")
682 .set_name("J/ApJ/701/1219")
683 .set_description(
684 r#"Photometric and spectroscopic catalog of objects in the field around HE0226-4110"#
685 .into(),
686 )
687 .push_coosys(CooSys::new("J2000", System::new_default_eq_fk5()))
688 .push_coosys(CooSys::new("J2015.5", System::new_icrs().set_epoch(2015.5)))
689 .insert_extra(
690 "toto",
691 Number::from_f64(0.5)
692 .map(Value::Number)
693 .unwrap_or(Value::Null),
694 )
695 .push_sub_elem(
696 ResourceSubElem::from_table(table)
697 .push_info(Info::new("matches", "50").set_content("matching records"))
698 .push_info(Info::new("Warning", "No center provided++++"))
699 .push_info(Info::new("Warning", "truncated result (maxtup=50)"))
700 .push_info(
701 Info::new("QUERY_STATUS", "OVERFLOW").set_content("truncated result (maxtup=50)"),
702 ),
703 );
704
705 let mut votable = VOTable::new(Version::V1_4, resource)
706 .set_id("my_votable")
707 .set_description(r#"
708 VizieR Astronomical Server vizier.u-strasbg.fr
709 Date: 2022-04-13T06:55:08 [V1.99+ (14-Oct-2013)]
710 Explanations and Statistics of UCDs: See LINK below
711 In case of problem, please report to: cds-question@unistra.fr
712 In this version, NULL integer columns are written as an empty string
713 <TD></TD>, explicitely possible from VOTable-1.3
714 "#.into()
715 )
716 .push_info(Info::new("votable-version", "1.99+ (14-Oct-2013)").set_id("VERSION"))
717 .push_info(Info::new("queryParameters", "25")
718 .set_content(r#"
719 -oc.form=dec
720 -out.max=50
721 -out.all=2
722 -nav=cat:J/ApJ/701/1219&tab:{J/ApJ/701/1219/table4}&key:source=J/ApJ/701/1219&HTTPPRM:&
723 -c.eq=J2000
724 -c.r= 2
725 -c.u=arcmin
726 -c.geom=r
727 -source=J/ApJ/701/1219/table4
728 -order=I
729 -out=ID
730 -out=RAJ2000
731 -out=DEJ2000
732 -out=Sep
733 -out=Dist
734 -out=Bmag
735 -out=e_Bmag
736 -out=Rmag
737 -out=e_Rmag
738 -out=Imag
739 -out=e_Imag
740 -out=z
741 -out=Type
742 -out=RMag
743 -out.all=2
744 "#));
745
746 println!("\n\n#### JSON ####\n");
747
748 match serde_json::to_string_pretty(&votable) {
749 Ok(content) => {
750 let mut votable2 =
752 serde_json::de::from_str::<VOTable<InMemTableDataRows>>(content.as_str()).unwrap();
753 votable2.ensures_consistency().unwrap();
754 let content2 = serde_json::to_string_pretty(&votable2).unwrap();
755
756 assert_eq!(content, content2);
760 assert_eq!(votable, votable2);
764 }
765 Err(error) => {
766 println!("{:?}", &error);
767 assert!(false);
768 }
769 }
770
771 println!("\n\n#### YAML ####\n");
772
773 match serde_yaml::to_string(&votable) {
774 Ok(content) => {
775 let votable2 =
777 serde_yaml::from_str::<VOTable<InMemTableDataRows>>(content.as_str()).unwrap();
778 let content2 = serde_yaml::to_string(&votable2).unwrap();
779 assert_eq!(content, content2);
780 }
781 Err(error) => {
782 println!("{:?}", &error);
783 assert!(false);
784 }
785 }
786
787 println!("\n\n#### VOTABLE ####\n");
788
789 let mut content = Vec::new();
790 let mut write = Writer::new_with_indent(&mut content, b' ', 4);
791 match votable.write(&mut write, &()) {
792 Ok(_) => {
793 let mut votable2 = VOTable::<InMemTableDataRows>::from_reader(content.as_slice()).unwrap();
796 let mut content2 = Vec::new();
797 let mut write2 = Writer::new_with_indent(&mut content2, b' ', 4);
798 votable2.write(&mut write2, &()).unwrap();
799
800 eprintln!("CONTENT1:\n{}", from_utf8(content.as_slice()).unwrap());
801 eprintln!("CONTENT2:\n{}", from_utf8(content2.as_slice()).unwrap());
802
803 assert_eq!(content, content2);
804 }
805 Err(error) => {
806 println!("Error: {:?}", &error);
807 assert!(false);
808 }
809 }
810
811 println!("\n\n#### TOML ####\n");
812
813 match toml::ser::to_string_pretty(&votable) {
814 Ok(content) => {
815 let votable2 = toml::de::from_str::<VOTable<InMemTableDataRows>>(content.as_str()).unwrap();
817 let content2 = toml::ser::to_string_pretty(&votable2).unwrap();
818 assert_eq!(content, content2);
819 }
820 Err(error) => {
821 println!("{:?}", &error);
822 assert!(false);
823 }
824 }
825
826 }
835
836 #[test]
838 fn test_create_in_mem_simple() {
839 let rows = vec![
840 vec![
841 VOTableValue::Double(f64::NAN),
842 VOTableValue::CharASCII('*'),
843 VOTableValue::Float(14.52),
844 ],
845 vec![
846 VOTableValue::Double(1.25),
847 VOTableValue::Null,
848 VOTableValue::Float(-1.2),
849 ],
850 ];
851 let data_content = InMemTableDataRows::new(rows);
852 let table = Table::new()
853 .set_id("V_147_sdss12")
854 .set_name("V/147/sdss12")
855 .set_description("SDSS photometric catalog".into())
856 .push_field(
857 Field::new("RA_ICRS", Datatype::Double)
858 .set_unit("deg")
859 .set_ucd("pos.eq.ra;meta.main")
860 .set_width(10)
861 .set_precision(Precision::new_dec(6))
862 .set_description("Right Ascension of the object (ICRS) (ra)".into())
863 ).push_field(
864 Field::new("m_SDSS12", Datatype::CharASCII)
865 .set_ucd("meta.code.multip")
866 .set_arraysize(ArraySize::new_fixed_1d(1))
867 .set_width(10)
868 .set_precision(Precision::new_dec(6))
869 .set_description("[*] Multiple SDSS12 name".into())
870 .push_link(Link::new().set_href("http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}"))
871 ).push_field(
872 Field::new("umag", Datatype::Float)
873 .set_unit("mag")
874 .set_ucd("phot.mag;em.opt.U")
875 .set_width(2)
876 .set_precision(Precision::new_dec(3))
877 .set_description("[4/38]? Model magnitude in u filter, AB scale (u) (5)".into())
878 .set_values(Values::new().set_null("NaN"))
879 ).set_data(Data::new_empty().set_tabledata(data_content));
880
881 let resource = Resource::default()
882 .set_id("yCat_17011219")
883 .set_name("J/ApJ/701/1219")
884 .set_description(
885 r#"Photometric and spectroscopic catalog of objects in the field around HE0226-4110"#
886 .into(),
887 )
888 .push_coosys(CooSys::new("J2000", System::new_default_eq_fk5()))
889 .push_coosys(CooSys::new("J2015.5", System::new_icrs().set_epoch(2015.5)))
890 .push_sub_elem(ResourceSubElem::from_table(table).push_info(
891 Info::new("QUERY_STATUS", "OVERFLOW").set_content("truncated result (maxtup=2)"),
892 ));
893
894 let votable = VOTable::new(Version::V1_4, resource)
895 .set_id("my_votable")
896 .set_description(r#"VizieR Astronomical Server vizier.u-strasbg.fr"#.into())
897 .push_info(Info::new("votable-version", "1.99+ (14-Oct-2013)").set_id("VERSION"))
898 .wrap();
899
900 println!("\n\n#### JSON ####\n");
901
902 match serde_json::to_string_pretty(&votable) {
903 Ok(content) => {
904 println!("{}", &content);
905 }
906 Err(error) => println!("{:?}", &error),
907 }
908
909 println!("\n\n#### YAML ####\n");
910
911 match serde_yaml::to_string(&votable) {
912 Ok(content) => {
913 println!("{}", &content);
914 }
915 Err(error) => println!("{:?}", &error),
916 }
917
918 println!("\n\n#### TOML ####\n");
919
920 match toml::ser::to_string_pretty(&votable) {
921 Ok(content) => {
922 println!("{}", &content);
923 }
924 Err(error) => println!("{:?}", &error),
925 }
926
927 println!("\n\n#### VOTABLE ####\n");
928 let mut write = Writer::new_with_indent(std::io::stdout(), b' ', 4);
929 match votable.unwrap().write(&mut write, &()) {
930 Ok(_) => println!("\nOK"),
931 Err(error) => println!("Error: {:?}", &error),
932 }
933
934 }
941
942 pub(crate) fn test_read<X>(xml: &str) -> X
943 where
944 X: VOTableElement + QuickXmlReadWrite<<X as VOTableElement>::MarkerType, Context = ()>,
945 {
946 let mut reader = Reader::from_reader(Cursor::new(xml.as_bytes()));
947 let mut buff: Vec<u8> = Vec::with_capacity(xml.len());
948 loop {
949 let mut event = reader.read_event(&mut buff).unwrap();
950 match &mut event {
951 Event::Start(e) if e.local_name() == X::TAG_BYTES => {
952 match X::from_event_start(e)
953 .and_then(|info| info.read_content(&mut reader, &mut buff, &()))
954 {
955 Err(e) => {
956 eprintln!("Error: {}", e.to_string());
957 assert!(false);
958 }
959 Ok(info) => return info,
960 }
961 }
962 Event::Empty(e) if e.local_name() == X::TAG_BYTES => {
963 let info = X::from_event_start(e).unwrap();
964 return info;
965 }
966 Event::Text(e) if e.escaped().is_empty() => (), Event::Comment(_) => (),
968 Event::DocType(_) => (),
969 Event::Decl(_) => (),
970 _ => {
971 println!("{:?}", event);
972 assert!(false);
973 }
974 }
975 }
976 }
977
978 pub(crate) fn test_writer<X>(mut writable: X, xml: &str)
979 where
980 X: VOTableElement + QuickXmlReadWrite<<X as VOTableElement>::MarkerType, Context = ()>,
981 {
982 let mut writer = Writer::new(Cursor::new(Vec::new()));
984 writable.write(&mut writer, &()).unwrap();
985 let output = writer.into_inner().into_inner();
986 let output_str = unsafe { std::str::from_utf8_unchecked(output.as_slice()) };
987 assert_eq!(output_str, xml);
988 }
989}