Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ruststep/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2018"

[dependencies]
nom = "6.1.0"
serde = { version = "1.0.124", features = ["derive"]}
thiserror = "1.0.24"

[dev-dependencies]
Expand Down
14 changes: 14 additions & 0 deletions ruststep/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
use crate::parser::TokenizeFailed;
use serde::de;
use std::fmt;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
TokenizeFailed(#[from] TokenizeFailed),

#[error("Error while deserialize STEP struct: {0}")]
DeserializeFailed(String),
}

impl de::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: fmt::Display,
{
Error::DeserializeFailed(msg.to_string())
}
}
128 changes: 128 additions & 0 deletions ruststep/src/parser/exchange/parameter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::parser::{combinator::*, token::*};
use nom::{branch::alt, combinator::value, Parser};
use serde::{de, forward_to_deserialize_any};

#[derive(Debug, Clone, PartialEq)]
pub enum UntypedParameter {
Expand Down Expand Up @@ -33,6 +34,50 @@ pub enum Parameter {
Omitted,
}

impl Parameter {
pub fn integer(i: i64) -> Self {
Parameter::Untyped(UntypedParameter::Integer(i))
}

pub fn real(x: f64) -> Self {
Parameter::Untyped(UntypedParameter::Real(x))
}

pub fn string(s: &str) -> Self {
Parameter::Untyped(UntypedParameter::String(s.to_string()))
}
}

impl From<i64> for Parameter {
fn from(value: i64) -> Self {
Self::integer(value)
}
}

impl From<f64> for Parameter {
fn from(value: f64) -> Self {
Self::real(value)
}
}

impl From<String> for Parameter {
fn from(value: String) -> Self {
Parameter::Untyped(UntypedParameter::String(value))
}
}

impl std::iter::FromIterator<Parameter> for Parameter {
fn from_iter<Iter: IntoIterator<Item = Parameter>>(iter: Iter) -> Self {
Parameter::Untyped(UntypedParameter::List(iter.into_iter().collect()))
}
}

impl<'a> std::iter::FromIterator<&'a Parameter> for Parameter {
fn from_iter<Iter: IntoIterator<Item = &'a Parameter>>(iter: Iter) -> Self {
iter.into_iter().cloned().collect()
}
}

/// parameter = [typed_parameter] | [untyped_parameter] | [omitted_parameter] .
pub fn parameter(input: &str) -> ParseResult<Parameter> {
alt((typed_parameter, untyped_parameter, omitted_parameter)).parse(input)
Expand Down Expand Up @@ -73,3 +118,86 @@ pub fn omitted_parameter(input: &str) -> ParseResult<Parameter> {
pub fn parameter_list(input: &str) -> ParseResult<Vec<Parameter>> {
comma_separated(parameter).parse(input)
}

impl<'de, 'param> de::Deserializer<'de> for &'param Parameter {
type Error = crate::error::Error;

fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
Parameter::Typed { name: _, ty: _ } => unimplemented!(),
Parameter::Untyped(p) => match p {
UntypedParameter::Integer(val) => visitor.visit_i64(*val),
UntypedParameter::Real(val) => visitor.visit_f64(*val),
UntypedParameter::String(val) => visitor.visit_str(val),
UntypedParameter::List(params) => {
let seq = de::value::SeqDeserializer::new(params.iter());
visitor.visit_seq(seq)
}
_ => unimplemented!(),
},
Parameter::Omitted => unimplemented!(),
}
}

forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}

impl<'de, 'param> de::IntoDeserializer<'de, crate::error::Error> for &'param Parameter {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}

#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;

#[test]
fn list_from_iter() {
let l: Parameter = [Parameter::integer(1), Parameter::real(2.0)]
.iter()
.collect();
assert!(matches!(l, Parameter::Untyped(UntypedParameter::List(_))));
}

#[test]
fn deserialize_int() {
let p = Parameter::Untyped(UntypedParameter::Integer(2));
let a: i64 = Deserialize::deserialize(&p).unwrap();
assert_eq!(a, 2);
// can be deserialized as unsigned
let a: u32 = Deserialize::deserialize(&p).unwrap();
assert_eq!(a, 2);

let p = Parameter::Untyped(UntypedParameter::Integer(-2));
let a: i64 = Deserialize::deserialize(&p).unwrap();
assert_eq!(a, -2);
// cannot be deserialized negative integer into unsigned
let res: Result<u32, _> = Deserialize::deserialize(&p);
assert!(res.is_err());
}

#[derive(Debug, Deserialize)]
struct A {
x: f64,
y: f64,
}

#[test]
fn deserialize_parameter_list_to_struct() {
let p: Parameter = [Parameter::real(1.0), Parameter::real(2.0)]
.iter()
.collect();
let a: A = Deserialize::deserialize(&p).unwrap();
dbg!(a);
}
}
2 changes: 1 addition & 1 deletion ruststep/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub mod token;
use nom::Finish;
use std::fmt;

pub use exchange::Record;
pub use exchange::{Parameter, Record, UntypedParameter};

/// Error while tokenizing STEP input
#[derive(Debug)]
Expand Down