1use std::{
2 fmt::Write,
3 fmt::{self, Display},
4};
5
6use serde::{Deserialize, Serialize};
7
8use nom::{
9 character::complete::multispace0,
10 combinator::{map, opt},
11 error::{FromExternalError, ParseError},
12 sequence::{preceded, tuple},
13 IResult,
14};
15
16pub mod common;
17pub mod redshift;
18pub mod space;
19pub mod spectral;
20pub mod time;
21pub mod visitor;
22
23use self::{
24 redshift::Redshift,
25 space::Space,
26 spectral::Spectral,
27 time::Time,
28 visitor::{RedshiftVisitor, SpaceVisitor, SpectralVisitor, StcVisitResult, TimeVisitor},
29};
30
31pub trait NomErr<'a>: ParseError<&'a str> + FromExternalError<&'a str, String> {}
33impl<'a, T> NomErr<'a> for T where T: ParseError<&'a str> + FromExternalError<&'a str, String> {}
35
36#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
38pub struct Stc {
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub time: Option<Time>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub space: Option<Space>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub spectral: Option<Spectral>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub redshift: Option<Redshift>,
47}
48impl Stc {
49 pub fn new() -> Self {
51 Self::default()
52 }
53 pub fn set_time<T: Into<Time>>(mut self, elem: T) -> Self {
55 self.set_time_by_ref(elem);
56 self
57 }
58 pub fn set_time_by_ref<T: Into<Time>>(&mut self, elem: T) {
60 self.time.replace(elem.into());
61 }
62
63 pub fn set_space<T: Into<Space>>(mut self, space: T) -> Self {
65 self.set_space_by_ref(space);
66 self
67 }
68 pub fn set_space_by_ref<T: Into<Space>>(&mut self, space: T) {
70 self.space.replace(space.into());
71 }
72
73 pub fn set_spectral<T: Into<Spectral>>(mut self, spectral: T) -> Self {
75 self.set_spectral_by_ref(spectral);
76 self
77 }
78 pub fn set_spectral_by_ref<T: Into<Spectral>>(&mut self, spectral: T) {
80 self.spectral.replace(spectral.into());
81 }
82
83 pub fn set_redshift<T: Into<Redshift>>(mut self, redshift: T) -> Self {
85 self.set_redshift_by_ref(redshift);
86 self
87 }
88 pub fn set_redshift_by_ref<T: Into<Redshift>>(&mut self, redshift: T) {
90 self.redshift.replace(redshift.into());
91 }
92
93 pub fn accept<T, S, P, R>(
94 &self,
95 time_visitor: T,
96 space_visitor: S,
97 spectral_visitor: P,
98 redshift_visitor: R,
99 ) -> StcVisitResult<T, S, P, R>
100 where
101 T: TimeVisitor,
102 S: SpaceVisitor,
103 P: SpectralVisitor,
104 R: RedshiftVisitor,
105 {
106 StcVisitResult::new(
107 self.time.as_ref().map(|time| time.accept(time_visitor)),
108 self.space.as_ref().map(|space| space.accept(space_visitor)),
109 self
110 .spectral
111 .as_ref()
112 .map(|spectral| spectral.accept(spectral_visitor)),
113 self
114 .redshift
115 .as_ref()
116 .map(|redshift| redshift.accept(redshift_visitor)),
117 )
118 }
119
120 pub fn parse<'a, E: NomErr<'a>>(input: &'a str) -> IResult<&'a str, Self, E> {
122 map(
123 tuple((
124 opt(preceded(multispace0, Time::parse::<E>)),
125 opt(preceded(multispace0, Space::parse::<E>)),
126 opt(preceded(multispace0, Spectral::parse::<E>)),
127 opt(preceded(multispace0, Redshift::parse::<E>)),
128 )),
129 |(time, space, spectral, redshift)| Self {
130 time,
131 space,
132 spectral,
133 redshift,
134 },
135 )(input)
136 }
137}
138impl Display for Stc {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 let mut first_sub_phrase = true;
141 if let Some(time) = &self.time {
142 Display::fmt(time, f)?;
143 first_sub_phrase = false
144 }
145 if let Some(space) = &self.space {
146 if first_sub_phrase {
147 first_sub_phrase = false;
148 } else {
149 f.write_char('\n')?;
150 }
151 Display::fmt(space, f)?;
152 }
153 if let Some(spectral) = &self.spectral {
154 if first_sub_phrase {
155 first_sub_phrase = false;
156 } else {
157 f.write_char('\n')?;
158 }
159 Display::fmt(spectral, f)?;
160 }
161 if let Some(redshift) = &self.redshift {
162 if !first_sub_phrase {
163 f.write_char('\n')?;
164 }
165 Display::fmt(redshift, f)?;
166 }
167 Ok(())
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::{
174 common::SpaceTimeRefPos,
175 space::{common::Frame, positioninterval::PositionInterval},
176 *,
177 };
178 use nom::{
179 error::{convert_error, VerboseError},
180 Err,
181 };
182
183 #[test]
184 fn test_api_1() {
185 let stc = Stc::new().set_space(
186 PositionInterval::from_frame(Frame::ICRS)
187 .set_refpos(SpaceTimeRefPos::Geocenter)
188 .set_lo_hi_limits(vec![170.0, -20.0, 190.0, 10.0])
189 .set_resolution(vec![0.0001]),
190 );
191 let s = "PositionInterval ICRS GEOCENTER 170 -20 190 10 Resolution 0.0001";
192 assert_eq!(stc.to_string().as_str(), s);
193 }
194
195 #[test]
196 fn test_from_stcs_doc_1() {
197 test(
198 "Circle ICRS TOPOCENTER 147.6 69.9 0.4",
199 r#"{
200 "space": {
201 "Circle": {
202 "frame": "ICRS",
203 "refpos": "TOPOCENTER",
204 "pos": [
205 147.6,
206 69.9
207 ],
208 "radius": 0.4
209 }
210 }
211}"#,
212 false,
213 );
214 }
215
216 #[test]
217 fn test_from_stcs_doc_2() {
218 test(
219 r#"Time TDB BARYCENTER MJD 50814.0 Position ICRS BARYCENTER 147.3 69.3"#,
220 r#"{
221 "time": {
222 "Time": {
223 "timescale": "TDB",
224 "refpos": "BARYCENTER",
225 "time": {
226 "MJD": "50814.0"
227 }
228 }
229 },
230 "space": {
231 "frame": "ICRS",
232 "refpos": "BARYCENTER",
233 "pos": [
234 147.3,
235 69.3
236 ]
237 }
238}"#,
239 false,
240 );
241 }
242 #[test]
243 fn test_from_stcs_doc_3() {
244 test(
245 r#"TimeInterval TT GEOCENTER 2011-01-01 2012-03-30 Resolution 0.2"#,
246 r#"{
247 "time": {
248 "TimeInterval": {
249 "timescale": "TT",
250 "refpos": "GEOCENTER",
251 "start": [
252 {
253 "Iso": "2011-01-01"
254 }
255 ],
256 "stop": [
257 {
258 "Iso": "2012-03-30"
259 }
260 ],
261 "resolution": 0.2
262 }
263 }
264}"#,
265 false,
266 );
267 }
268
269 #[test]
270 fn test_from_stcs_doc_4() {
271 test(
272 r#"TimeInterval TT GEOCENTER 2011-01-01 2012-03-30 Resolution 0.2
273PositionInterval ICRS GEOCENTER 170 -20 190 10 Resolution 0.0001
274SpectralInterval GEOCENTER 4.95 5 unit GHz Size 0.05"#,
275 r#"{
276 "time": {
277 "TimeInterval": {
278 "timescale": "TT",
279 "refpos": "GEOCENTER",
280 "start": [
281 {
282 "Iso": "2011-01-01"
283 }
284 ],
285 "stop": [
286 {
287 "Iso": "2012-03-30"
288 }
289 ],
290 "resolution": 0.2
291 }
292 },
293 "space": {
294 "frame": "ICRS",
295 "refpos": "GEOCENTER",
296 "lo_hi_limits": [
297 170.0,
298 -20.0,
299 190.0,
300 10.0
301 ],
302 "resolution": [
303 0.0001
304 ]
305 },
306 "spectral": {
307 "Interval": {
308 "refpos": "GEOCENTER",
309 "lolimit": [
310 4.95
311 ],
312 "hilimit": [
313 5.0
314 ],
315 "unit": "GHz",
316 "size": 0.05
317 }
318 }
319}"#,
320 false,
321 );
322 }
323
324 #[test]
325 fn test_from_stcs_doc_5() {
326 test(
327 r#"Union ICRS TOPOCENTER
328( Polygon 147.8 69.2 147.4 69.2 147.3 69.4 147.9 69.4
329Polygon 147.9 69.7 147.6 69.7 147.5 69.9 148 69.9 )"#,
330 r#"{
331 "space": {
332 "Union": {
333 "frame": "ICRS",
334 "refpos": "TOPOCENTER",
335 "elems": [
336 {
337 "Polygon": {
338 "pos": [
339 147.8,
340 69.2,
341 147.4,
342 69.2,
343 147.3,
344 69.4,
345 147.9,
346 69.4
347 ]
348 }
349 },
350 {
351 "Polygon": {
352 "pos": [
353 147.9,
354 69.7,
355 147.6,
356 69.7,
357 147.5,
358 69.9,
359 148.0,
360 69.9
361 ]
362 }
363 }
364 ]
365 }
366 }
367}"#,
368 false,
369 );
370 }
371
372 #[test]
373 fn test_from_stcs_doc_6() {
374 test(
375 r#"Union ICRS TOPOCENTER
376( Circle 180 10 20
377Circle 190 20 20
378Intersection
379( Circle 120 -10 20
380Difference
381( Circle 130 -10 20
382Circle 125 -10 2
383)
384Not
385( Circle 118 -8 3 )
386)
387)"#,
388 r#"{
389 "space": {
390 "Union": {
391 "frame": "ICRS",
392 "refpos": "TOPOCENTER",
393 "elems": [
394 {
395 "Circle": {
396 "pos": [
397 180.0,
398 10.0
399 ],
400 "radius": 20.0
401 }
402 },
403 {
404 "Circle": {
405 "pos": [
406 190.0,
407 20.0
408 ],
409 "radius": 20.0
410 }
411 },
412 {
413 "Intersection": {
414 "elems": [
415 {
416 "Circle": {
417 "pos": [
418 120.0,
419 -10.0
420 ],
421 "radius": 20.0
422 }
423 },
424 {
425 "Difference": {
426 "left": {
427 "Circle": {
428 "pos": [
429 130.0,
430 -10.0
431 ],
432 "radius": 20.0
433 }
434 },
435 "right": {
436 "Circle": {
437 "pos": [
438 125.0,
439 -10.0
440 ],
441 "radius": 2.0
442 }
443 }
444 }
445 },
446 {
447 "Not": {
448 "Circle": {
449 "pos": [
450 118.0,
451 -8.0
452 ],
453 "radius": 3.0
454 }
455 }
456 }
457 ]
458 }
459 }
460 ]
461 }
462 }
463}"#,
464 false,
465 );
466 }
467
468 #[test]
469 fn test_from_stcs_doc_7() {
470 test(
471 r#"TimeInterval TT GEOCENTER 1996-01-01T00:00:00Z 1996-01-01T00:30:00Z
472 Time MJD 50814.0 Error 1.2 Resolution 0.8 PixSize 1024
473Circle ICRS GEOCENTER 179 -11.5 0.5 Position 179 -11.5
474 Error 0.000889 Resolution 0.001778 Size 0.000333 0.000278
475 PixSize 0.000083 0.000083
476Spectral BARYCENTER 1420.4 unit MHz Resolution 10
477RedshiftInterval BARYCENTER VELOCITY OPTICAL 200 2300
478 Redshift 300 Resolution 0.7 PixSize 0.3"#,
479 r#"{
480 "time": {
481 "TimeInterval": {
482 "timescale": "TT",
483 "refpos": "GEOCENTER",
484 "start": [
485 {
486 "Iso": "1996-01-01T00:00:00Z"
487 }
488 ],
489 "stop": [
490 {
491 "Iso": "1996-01-01T00:30:00Z"
492 }
493 ],
494 "time": {
495 "MJD": "50814.0"
496 },
497 "error": 1.2,
498 "resolution": 0.8,
499 "pixsize": 1024.0
500 }
501 },
502 "space": {
503 "Circle": {
504 "frame": "ICRS",
505 "refpos": "GEOCENTER",
506 "pos": [
507 179.0,
508 -11.5
509 ],
510 "radius": 0.5,
511 "position": [
512 179.0,
513 -11.5
514 ],
515 "error": [
516 0.000889
517 ],
518 "resolution": [
519 0.001778
520 ],
521 "size": [
522 0.000333,
523 0.000278
524 ],
525 "pixsize": [
526 0.000083,
527 0.000083
528 ]
529 }
530 },
531 "spectral": {
532 "Value": {
533 "refpos": "BARYCENTER",
534 "value": 1420.4,
535 "unit": "MHz",
536 "resolution": 10.0
537 }
538 },
539 "redshift": {
540 "RedshiftInterval": {
541 "refpos": "BARYCENTER",
542 "type": "VELOCITY",
543 "dopplerdef": "OPTICAL",
544 "lolimit": [
545 200.0
546 ],
547 "hilimit": [
548 2300.0
549 ],
550 "redshift": 300.0,
551 "resolution": 0.7,
552 "pixsize": 0.3
553 }
554 }
555}"#,
556 false,
557 );
558 }
559
560 #[test]
561 fn test_from_stcs_doc_8() {
562 test(
564 r#"StartTime TT BARYCENTER 1900-01-01
565Circle ICRS BARYCENTER 148.9 69.1 2.0
566 Resolution 0.0001 0.0001 0.0003 0.0003 Size 0.5 0.5 0.67 0.67
567 PixSize 0.00005 0.00005 0.00015 0.00015
568SpectralInterval BARYCENTER 4000 7000 unit Angstrom Resolution 300 600"#,
569 r#"{
570 "time": {
571 "StartTime": {
572 "timescale": "TT",
573 "refpos": "BARYCENTER",
574 "start": {
575 "Iso": "1900-01-01"
576 }
577 }
578 },
579 "space": {
580 "Circle": {
581 "frame": "ICRS",
582 "refpos": "BARYCENTER",
583 "pos": [
584 148.9,
585 69.1
586 ],
587 "radius": 2.0,
588 "resolution": [
589 0.0001,
590 0.0001,
591 0.0003,
592 0.0003
593 ],
594 "size": [
595 0.5,
596 0.5,
597 0.67,
598 0.67
599 ],
600 "pixsize": [
601 0.00005,
602 0.00005,
603 0.00015,
604 0.00015
605 ]
606 }
607 },
608 "spectral": {
609 "Interval": {
610 "refpos": "BARYCENTER",
611 "lolimit": [
612 4000.0
613 ],
614 "hilimit": [
615 7000.0
616 ],
617 "unit": "Angstrom",
618 "resolution": {
619 "lo": 300.0,
620 "hi": 600.0
621 }
622 }
623 }
624}"#,
625 false,
626 );
627 }
628
629 #[test]
630 fn test_from_tap_section6_1() {
631 test(
632 r#"Circle ICRS GEOCENTER 10 20 0.5"#,
633 r#"{
634 "space": {
635 "Circle": {
636 "frame": "ICRS",
637 "refpos": "GEOCENTER",
638 "pos": [
639 10.0,
640 20.0
641 ],
642 "radius": 0.5
643 }
644 }
645}"#,
646 false,
647 );
648 }
649
650 #[test]
651 fn test_from_tap_section6_2() {
652 test(
653 r#"Position GALACTIC 10 20"#,
654 r#"{
655 "space": {
656 "frame": "GALACTIC",
657 "pos": [
658 10.0,
659 20.0
660 ]
661 }
662}"#,
663 false,
664 );
665 }
666
667 #[test]
668 fn test_from_tap_section6_3() {
669 test(
670 r#"Box CARTESIAN2 3 3 2 2"#,
671 r#"{
672 "space": {
673 "Box": {
674 "frame": "UNKNOWNFrame",
675 "flavor": "CART2",
676 "pos": [
677 3.0,
678 3.0
679 ],
680 "bsize": [
681 2.0,
682 2.0
683 ]
684 }
685 }
686}"#,
687 false,
688 );
689 }
690
691 #[test]
692 fn test_from_tap_section6_4() {
693 test(
694 r#"Box 180 0 2 2"#,
695 r#"{
696 "space": {
697 "Box": {
698 "frame": "UNKNOWNFrame",
699 "pos": [
700 180.0,
701 0.0
702 ],
703 "bsize": [
704 2.0,
705 2.0
706 ]
707 }
708 }
709}"#,
710 false,
711 );
712 }
713
714 #[test]
715 fn test_from_tap_section6_5() {
716 test(
717 r#"Union ICRS ( Polygon 1 4 2 4 2 5 1 5 Polygon 3 4 4 4 4 5 3 5 )"#,
718 r#"{
719 "space": {
720 "Union": {
721 "frame": "ICRS",
722 "elems": [
723 {
724 "Polygon": {
725 "pos": [
726 1.0,
727 4.0,
728 2.0,
729 4.0,
730 2.0,
731 5.0,
732 1.0,
733 5.0
734 ]
735 }
736 },
737 {
738 "Polygon": {
739 "pos": [
740 3.0,
741 4.0,
742 4.0,
743 4.0,
744 4.0,
745 5.0,
746 3.0,
747 5.0
748 ]
749 }
750 }
751 ]
752 }
753 }
754}"#,
755 false,
756 );
757 }
758
759 fn test(ascii_str: &str, json_str: &str, print_json: bool) {
760 match Stc::parse::<VerboseError<&str>>(ascii_str) {
761 Ok((rem, space)) => {
762 assert_eq!(rem, "", "Remaining: {}", rem);
764 assert_eq!(
766 space
767 .to_string()
768 .replace('\n', " ")
769 .replace(" ", " ")
770 .replace(".0 ", " ")
771 .replace(" UNKNOWNFrame", ""),
772 ascii_str
773 .replace('\n', " ")
774 .replace(" ", " ")
775 .replace(".0 ", " ")
776 .replace("CARTESIAN", "CART")
777 );
778 let json = serde_json::to_string_pretty(&space).unwrap();
780 if print_json {
781 println!("Json:\n{}", json);
782 }
783 assert_eq!(json, json_str);
784 let space2 = serde_json::from_str(&json).unwrap();
786 assert_eq!(space, space2);
787 }
797 Err(err) => {
798 println!("Error: {:#?}", err);
799 match err {
800 Err::Incomplete(e) => println!("Error: {:?}", e),
801 Err::Error(e) => println!("Error: {}", convert_error(ascii_str, e)),
802 Err::Failure(e) => println!("Error: {}", convert_error(ascii_str, e)),
803 };
804 assert!(false)
805 }
806 }
807 }
808}