Skip to content

Commit

Permalink
Add API elements and Visitor
Browse files Browse the repository at this point in the history
  • Loading branch information
fxpineau committed Dec 8, 2023
1 parent 3ce1dbe commit c8208db
Show file tree
Hide file tree
Showing 23 changed files with 3,251 additions and 352 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,27 @@ We are (reasonably) open to changes and we appreciate feedbacks.
Please, provide us with more STC-S examples!


## STC-S String from API example

```rust
Stc::new().set_space(
PositionInterval::from_frame(Frame::ICRS)
.set_refpos(SpaceTimeRefPos::Geocenter)
.set_lo_hi_limits(vec![170.0, -20.0, 190.0, 10.0])
.set_resolution(vec![0.0001])
).to_string();
```
gives the string:
```txt
"PositionInterval ICRS GEOCENTER 170 -20 190 10 Resolution 0.0001"
```
You can parse the string back to an object using (`unwrap` must be avoided, see tests in `lib.rs`
for a complete example):
```rust
let ascii = "PositionInterval ICRS GEOCENTER 170 -20 190 10 Resolution 0.0001";
let stc = Stc::parse::<VerboseError<&str>>(ascii).unwrap().1;
```

## STC-S to JSON conversion examples

The following examples are extracted from the internal tests.
Expand Down Expand Up @@ -287,8 +308,10 @@ is converted into:
* [X] Support parsing of STC-S as defined in TAP.
* [ ] Make a CLI.
* [ ] Make a JS/Wasm library.
* [ ] Add everywhere builders, getters and setters like in the `FillFrameRefposFlavor` structure to make a clean API.
* [ ] Create a visitor.
* [X] Add everywhere builders, getters and setters like in the `FillFrameRefposFlavor` structure to make a clean API.
* [X] Create a visitor.
+ [X] implement an 'empty' visitor
+ [ ] implement an 'echo' visitor
* [ ] Implement `fold` to avoid too wide lines.
* [ ] Support for STC XML serialization/deserialization?

Expand Down
84 changes: 47 additions & 37 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{

use serde::{Deserialize, Serialize};

use nom::{branch::alt, bytes::complete::tag, combinator::value, IResult};
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::value, IResult};

use super::NomErr;

Expand Down Expand Up @@ -42,7 +42,7 @@ impl From<Vec<f64>> for ValOrRange {
}

/// The default value is `UNKNOWNRefPos`
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
pub enum SpaceTimeRefPos {
#[serde(rename = "GEOCENTER")]
Geocenter,
Expand Down Expand Up @@ -77,25 +77,30 @@ pub enum SpaceTimeRefPos {
#[serde(rename = "UNKNOWNRefPos")]
UnknownRefPos,
}
impl Default for SpaceTimeRefPos {
fn default() -> Self {
Self::UnknownRefPos
}
}
impl SpaceTimeRefPos {
pub fn parse<'a, E: NomErr<'a>>(input: &'a str) -> IResult<&'a str, Self, E> {
alt((
value(Self::Geocenter, tag("GEOCENTER")),
value(Self::Barycenter, tag("BARYCENTER")),
value(Self::Heliocenter, tag("HELIOCENTER")),
value(Self::Topocenter, tag("TOPOCENTER")),
value(Self::GalacticCenter, tag("GALACTIC_CENTER")),
value(Self::Embarycenter, tag("EMBARYCENTER")),
value(Self::Moon, tag("MOON")),
value(Self::Mercury, tag("MERCURY")),
value(Self::Venus, tag("VENUS")),
value(Self::Mars, tag("MARS")),
value(Self::Jupiter, tag("JUPITER")),
value(Self::Saturn, tag("SATURN")),
value(Self::Uranus, tag("URANUS")),
value(Self::Neptune, tag("NEPTUNE")),
value(Self::Pluto, tag("PLUTO")),
value(Self::UnknownRefPos, tag("UNKNOWNRefPos")),
value(Self::Geocenter, tag_no_case("GEOCENTER")),
value(Self::Barycenter, tag_no_case("BARYCENTER")),
value(Self::Heliocenter, tag_no_case("HELIOCENTER")),
value(Self::Topocenter, tag_no_case("TOPOCENTER")),
value(Self::GalacticCenter, tag_no_case("GALACTIC_CENTER")),
value(Self::Embarycenter, tag_no_case("EMBARYCENTER")),
value(Self::Moon, tag_no_case("MOON")),
value(Self::Mercury, tag_no_case("MERCURY")),
value(Self::Venus, tag_no_case("VENUS")),
value(Self::Mars, tag_no_case("MARS")),
value(Self::Jupiter, tag_no_case("JUPITER")),
value(Self::Saturn, tag_no_case("SATURN")),
value(Self::Uranus, tag_no_case("URANUS")),
value(Self::Neptune, tag_no_case("NEPTUNE")),
value(Self::Pluto, tag_no_case("PLUTO")),
value(Self::UnknownRefPos, tag_no_case("UNKNOWNRefPos")),
))(input)
}
}
Expand Down Expand Up @@ -194,28 +199,33 @@ pub enum SpectralRedshiftRefPos {
#[serde(rename = "UNKNOWNRefPos")]
UnknownRefPos,
}
impl Default for SpectralRedshiftRefPos {
fn default() -> Self {
Self::UnknownRefPos
}
}
impl SpectralRedshiftRefPos {
pub fn parse<'a, E: NomErr<'a>>(input: &'a str) -> IResult<&'a str, Self, E> {
alt((
value(Self::Geocenter, tag("GEOCENTER")),
value(Self::Barycenter, tag("BARYCENTER")),
value(Self::Heliocenter, tag("HELIOCENTER")),
value(Self::Topocenter, tag("TOPOCENTER")),
value(Self::LsrK, tag("LSR[K]")),
value(Self::Lsrd, tag("LSRD")),
value(Self::GalacticCenter, tag("GALACTIC_CENTER")),
value(Self::LocalGroupCenter, tag("LOCAL_GROUP_CENTER")),
value(Self::Embarycenter, tag("EMBARYCENTER")),
value(Self::Moon, tag("MOON")),
value(Self::Mercury, tag("MERCURY")),
value(Self::Venus, tag("VENUS")),
value(Self::Mars, tag("MARS")),
value(Self::Jupiter, tag("JUPITER")),
value(Self::Saturn, tag("SATURN")),
value(Self::Uranus, tag("URANUS")),
value(Self::Neptune, tag("NEPTUNE")),
value(Self::Pluto, tag("PLUTO")),
value(Self::UnknownRefPos, tag("UNKNOWNRefPos")),
value(Self::Geocenter, tag_no_case("GEOCENTER")),
value(Self::Barycenter, tag_no_case("BARYCENTER")),
value(Self::Heliocenter, tag_no_case("HELIOCENTER")),
value(Self::Topocenter, tag_no_case("TOPOCENTER")),
value(Self::LsrK, tag_no_case("LSR[K]")),
value(Self::Lsrd, tag_no_case("LSRD")),
value(Self::GalacticCenter, tag_no_case("GALACTIC_CENTER")),
value(Self::LocalGroupCenter, tag_no_case("LOCAL_GROUP_CENTER")),
value(Self::Embarycenter, tag_no_case("EMBARYCENTER")),
value(Self::Moon, tag_no_case("MOON")),
value(Self::Mercury, tag_no_case("MERCURY")),
value(Self::Venus, tag_no_case("VENUS")),
value(Self::Mars, tag_no_case("MARS")),
value(Self::Jupiter, tag_no_case("JUPITER")),
value(Self::Saturn, tag_no_case("SATURN")),
value(Self::Uranus, tag_no_case("URANUS")),
value(Self::Neptune, tag_no_case("NEPTUNE")),
value(Self::Pluto, tag_no_case("PLUTO")),
value(Self::UnknownRefPos, tag_no_case("UNKNOWNRefPos")),
))(input)
}
}
Expand Down
104 changes: 82 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,62 +18,106 @@ pub mod redshift;
pub mod space;
pub mod spectral;
pub mod time;
pub mod visitor;

use self::{redshift::Redshift, space::Space, spectral::Spectral, time::Time};
use self::{
redshift::Redshift,
space::Space,
spectral::Spectral,
time::Time,
visitor::{RedshiftVisitor, SpaceVisitor, SpectralVisitor, StcVisitResult, TimeVisitor},
};

/// Trait used everywhere to propagate Nom errors.
pub trait NomErr<'a>: ParseError<&'a str> + FromExternalError<&'a str, String> {}

/// Implements the `NomErr` trait to all elements implementing both `ParseError` and `FromExternalError`.
impl<'a, T> NomErr<'a> for T where T: ParseError<&'a str> + FromExternalError<&'a str, String> {}

/// STC-S phrase.
/// Represents a STC-S phrase.
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct Stc {
#[serde(skip_serializing_if = "Option::is_none")]
time: Option<Time>,
pub time: Option<Time>,
#[serde(skip_serializing_if = "Option::is_none")]
space: Option<Space>,
pub space: Option<Space>,
#[serde(skip_serializing_if = "Option::is_none")]
spectral: Option<Spectral>,
pub spectral: Option<Spectral>,
#[serde(skip_serializing_if = "Option::is_none")]
redshift: Option<Redshift>,
pub redshift: Option<Redshift>,
}
impl Stc {
/// Create a new, empty, STC-S phrase.
pub fn new() -> Self {
Self::default()
}

pub fn set_time(mut self, time: Time) -> Self {
self.set_time_by_ref(time);
/// Set (or overwrite) the time sub-phrase.
pub fn set_time<T: Into<Time>>(mut self, elem: T) -> Self {
self.set_time_by_ref(elem);
self
}
pub fn set_time_by_ref(&mut self, time: Time) {
self.time.replace(time);
/// Set (or overwrite) the time sub-phrase, by reference.
pub fn set_time_by_ref<T: Into<Time>>(&mut self, elem: T) {
self.time.replace(elem.into());
}

pub fn set_space(mut self, space: Space) -> Self {
/// Set (or overwrite) the space sub-phrase.
pub fn set_space<T: Into<Space>>(mut self, space: T) -> Self {
self.set_space_by_ref(space);
self
}
pub fn set_space_by_ref(&mut self, space: Space) {
self.space.replace(space);
/// Set (or overwrite) the time sub-phrase, by reference.
pub fn set_space_by_ref<T: Into<Space>>(&mut self, space: T) {
self.space.replace(space.into());
}

pub fn set_spectral(mut self, spectral: Spectral) -> Self {
/// Set (or overwrite) the spectral sub-phrase.
pub fn set_spectral<T: Into<Spectral>>(mut self, spectral: T) -> Self {
self.set_spectral_by_ref(spectral);
self
}
pub fn set_spectral_by_ref(&mut self, spectral: Spectral) {
self.spectral.replace(spectral);
/// Set (or overwrite) the spectral sub-phrase, by reference.
pub fn set_spectral_by_ref<T: Into<Spectral>>(&mut self, spectral: T) {
self.spectral.replace(spectral.into());
}

pub fn set_redshift(mut self, redshift: Redshift) -> Self {
/// Set (or overwrite) the redshift sub-phrase.
pub fn set_redshift<T: Into<Redshift>>(mut self, redshift: T) -> Self {
self.set_redshift_by_ref(redshift);
self
}
pub fn set_redshift_by_ref(&mut self, redshift: Redshift) {
self.redshift.replace(redshift);
/// Set (or overwrite) the redshift sub-phrase, by reference.
pub fn set_redshift_by_ref<T: Into<Redshift>>(&mut self, redshift: T) {
self.redshift.replace(redshift.into());
}

pub fn accept<T, S, P, R>(
&self,
time_visitor: T,
space_visitor: S,
spectral_visitor: P,
redshift_visitor: R,
) -> StcVisitResult<T, S, P, R>
where
T: TimeVisitor,
S: SpaceVisitor,
P: SpectralVisitor,
R: RedshiftVisitor,
{
StcVisitResult::new(
self.time.as_ref().map(|time| time.accept(time_visitor)),
self.space.as_ref().map(|space| space.accept(space_visitor)),
self
.spectral
.as_ref()
.map(|spectral| spectral.accept(spectral_visitor)),
self
.redshift
.as_ref()
.map(|redshift| redshift.accept(redshift_visitor)),
)
}

/// Parse a complete STC-S Phrase.
pub fn parse<'a, E: NomErr<'a>>(input: &'a str) -> IResult<&'a str, Self, E> {
map(
tuple((
Expand Down Expand Up @@ -126,12 +170,28 @@ impl Display for Stc {

#[cfg(test)]
mod tests {
use super::*;
use super::{
common::SpaceTimeRefPos,
space::{common::Frame, positioninterval::PositionInterval},
*,
};
use nom::{
error::{convert_error, VerboseError},
Err,
};

#[test]
fn test_api_1() {
let stc = Stc::new().set_space(
PositionInterval::from_frame(Frame::ICRS)
.set_refpos(SpaceTimeRefPos::Geocenter)
.set_lo_hi_limits(vec![170.0, -20.0, 190.0, 10.0])
.set_resolution(vec![0.0001]),
);
let s = "PositionInterval ICRS GEOCENTER 170 -20 190 10 Resolution 0.0001";
assert_eq!(stc.to_string().as_str(), s);
}

#[test]
fn test_from_stcs_doc_1() {
test(
Expand Down
Loading

0 comments on commit c8208db

Please sign in to comment.