pub mod de;
pub mod enc;
pub fn decode<T: crate::Decode>(input: &str) -> Result<T, crate::error::DecodeError> {
T::decode(&mut de::Decoder::new(input)?)
}
pub fn encode<T: crate::Encode>(
value: &T,
) -> Result<alloc::string::String, crate::error::EncodeError> {
let mut encoder = enc::Encoder::new();
value.encode(&mut encoder)?;
Ok(encoder.to_string())
}
#[cfg(test)]
mod tests {
macro_rules! round_trip_jer {
($typ:ty, $value:expr, $expected:expr) => {{
let value: $typ = $value;
pretty_assertions::assert_eq!(value, round_trip_value!($typ, $value, $expected));
}};
}
macro_rules! round_trip_value {
($typ:ty, $value:expr, $expected:expr) => {{
let value: $typ = $value;
let expected: &'static str = $expected;
let actual_encoding = crate::jer::encode(&value).unwrap();
pretty_assertions::assert_eq!(expected, &*actual_encoding);
let decoded_value: $typ = crate::jer::decode(&actual_encoding).unwrap();
decoded_value
}};
}
macro_rules! round_trip_string_type {
($typ:ty) => {{
let string = String::from(" 1234567890");
let expected: &'static str = "\" 1234567890\"";
let value: $typ = string.try_into().unwrap();
let actual_encoding = crate::jer::encode(&value).unwrap();
pretty_assertions::assert_eq!(expected, &actual_encoding);
let decoded_value: $typ = crate::jer::decode(&actual_encoding).unwrap();
pretty_assertions::assert_eq!(value, decoded_value);
}};
}
use crate::prelude::*;
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(automatic_tags)]
#[rasn(crate_root = "crate")]
#[non_exhaustive]
struct TestTypeA {
#[rasn(value("0..3", extensible))]
juice: Integer,
wine: Inner,
#[rasn(extension_addition)]
grappa: BitString,
}
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(choice, automatic_tags)]
#[rasn(crate_root = "crate")]
enum Inner {
#[rasn(value("0..3"))]
Wine(u8),
}
#[derive(AsnType, Decode, Encode, Debug, Clone, Copy, PartialEq)]
#[rasn(automatic_tags, enumerated)]
#[rasn(crate_root = "crate")]
enum SimpleEnum {
Test1 = 5,
Test2 = 2,
}
#[derive(AsnType, Decode, Encode, Debug, Clone, Copy, PartialEq)]
#[rasn(automatic_tags, enumerated)]
#[rasn(crate_root = "crate")]
#[non_exhaustive]
enum ExtEnum {
Test1 = 5,
Test2 = 2,
#[rasn(extension_addition)]
Test3 = -1,
}
#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq, Ord, Eq, PartialOrd, Hash)]
#[rasn(automatic_tags, choice)]
#[rasn(crate_root = "crate")]
enum SimpleChoice {
Test1(u8),
#[rasn(size("0..3"))]
Test2(Utf8String),
}
#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)]
#[rasn(automatic_tags, choice)]
#[rasn(crate_root = "crate")]
#[non_exhaustive]
enum ExtChoice {
Test1(u8),
#[rasn(size("0..3"))]
Test2(Utf8String),
#[rasn(extension_addition)]
Test3(bool),
}
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(automatic_tags)]
#[rasn(crate_root = "crate")]
#[non_exhaustive]
struct Very {
#[rasn(extension_addition)]
a: Option<Nested>,
}
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(automatic_tags)]
#[rasn(crate_root = "crate")]
struct Nested {
very: Option<Struct>,
nested: Option<bool>,
}
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(automatic_tags)]
#[rasn(crate_root = "crate")]
struct Struct {
strct: Option<u8>,
}
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(crate_root = "crate", delegate, size("3", extensible))]
struct ConstrainedOctetString(pub OctetString);
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(crate_root = "crate", delegate, value("-5..=5", extensible))]
struct ConstrainedInt(pub Integer);
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(crate_root = "crate", delegate, size("3"))]
struct ConstrainedBitString(pub BitString);
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(automatic_tags)]
#[rasn(crate_root = "crate")]
struct Renamed {
#[rasn(identifier = "so-very")]
very: Integer,
#[rasn(identifier = "re_named")]
renamed: Option<bool>,
}
#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)]
#[rasn(automatic_tags, choice)]
#[rasn(crate_root = "crate")]
enum Renumed {
#[rasn(identifier = "test-1")]
#[rasn(size("0..3"))]
Test1(Utf8String),
}
#[test]
fn bool() {
round_trip_jer!(bool, true, "true");
round_trip_jer!(bool, false, "false");
}
#[test]
fn integer() {
round_trip_jer!(u8, 1, "1");
round_trip_jer!(i8, -1, "-1");
round_trip_jer!(u16, 0, "0");
round_trip_jer!(i16, -14321, "-14321");
round_trip_jer!(i64, -1_213_428_598_524_996_264, "-1213428598524996264");
round_trip_jer!(Integer, 1.into(), "1");
round_trip_jer!(Integer, (-1_235_352).into(), "-1235352");
round_trip_jer!(ConstrainedInt, ConstrainedInt(1.into()), "1");
}
#[test]
#[cfg(feature = "f32")]
fn real_f32() {
round_trip_jer!(f32, 0.0, "0.0");
round_trip_jer!(f32, -0.0, "\"-0\"");
round_trip_jer!(f32, f32::INFINITY, "\"INF\"");
round_trip_jer!(f32, f32::NEG_INFINITY, "\"-INF\"");
assert!(round_trip_value!(f32, f32::NAN, "\"NAN\"").is_nan());
round_trip_jer!(f32, 1.0, "1.0");
round_trip_jer!(f32, -1.0, "-1.0");
}
#[test]
#[cfg(feature = "f64")]
fn real_f64() {
round_trip_jer!(f64, 0.0, "0.0");
round_trip_jer!(f64, -0.0, "\"-0\"");
round_trip_jer!(f64, f64::INFINITY, "\"INF\"");
round_trip_jer!(f64, f64::NEG_INFINITY, "\"-INF\"");
assert!(round_trip_value!(f64, f64::NAN, "\"NAN\"").is_nan());
round_trip_jer!(f64, 1.0, "1.0");
round_trip_jer!(f64, -1.0, "-1.0");
}
#[test]
fn bit_string() {
round_trip_jer!(
BitString,
[true, false].into_iter().collect::<BitString>(),
r#"{"length":2,"value":"80"}"#
);
round_trip_jer!(
ConstrainedBitString,
ConstrainedBitString([true, false, true].into_iter().collect::<BitString>()),
"\"A0\""
);
}
#[test]
fn octet_string() {
round_trip_jer!(OctetString, OctetString::from_static(&[1, 255]), "\"01FF\"");
round_trip_jer!(
ConstrainedOctetString,
ConstrainedOctetString(OctetString::from_static(&[1, 255, 0, 254])),
"\"01FF00FE\""
);
}
#[test]
fn object_identifier() {
round_trip_jer!(
ObjectIdentifier,
ObjectIdentifier::from(Oid::JOINT_ISO_ITU_T_DS_NAME_FORM),
"\"2.5.15\""
);
}
#[test]
fn string_types() {
round_trip_string_type!(NumericString);
round_trip_string_type!(GeneralString);
round_trip_string_type!(VisibleString);
round_trip_string_type!(UniversalString);
round_trip_string_type!(PrintableString);
round_trip_string_type!(Ia5String);
round_trip_string_type!(Utf8String);
}
#[test]
fn enumerated() {
round_trip_jer!(SimpleEnum, SimpleEnum::Test1, "\"Test1\"");
round_trip_jer!(SimpleEnum, SimpleEnum::Test2, "\"Test2\"");
round_trip_jer!(ExtEnum, ExtEnum::Test1, "\"Test1\"");
round_trip_jer!(ExtEnum, ExtEnum::Test2, "\"Test2\"");
round_trip_jer!(ExtEnum, ExtEnum::Test3, "\"Test3\"");
}
#[test]
fn choice() {
round_trip_jer!(SimpleChoice, SimpleChoice::Test1(3), "{\"Test1\":3}");
round_trip_jer!(
SimpleChoice,
SimpleChoice::Test2("foo".into()),
"{\"Test2\":\"foo\"}"
);
round_trip_jer!(ExtChoice, ExtChoice::Test1(255), "{\"Test1\":255}");
round_trip_jer!(
ExtChoice,
ExtChoice::Test2("bar".into()),
"{\"Test2\":\"bar\"}"
);
round_trip_jer!(ExtChoice, ExtChoice::Test3(true), "{\"Test3\":true}");
}
#[test]
fn sequence_of() {
round_trip_jer!(
SequenceOf<SimpleChoice>,
alloc::vec![SimpleChoice::Test1(3)],
"[{\"Test1\":3}]"
);
round_trip_jer!(
SequenceOf<u8>,
alloc::vec![1, 2, 3, 4, 5, 5, 3],
"[1,2,3,4,5,5,3]"
);
round_trip_jer!(SequenceOf<bool>, alloc::vec![], "[]");
}
#[test]
fn set_of() {
round_trip_jer!(
SetOf<SimpleChoice>,
SetOf::from_vec(alloc::vec![SimpleChoice::Test1(3)]),
"[{\"Test1\":3}]"
);
let set = SetOf::from_vec(alloc::vec![1, 2, 3, 4, 5]);
let actual_encoding = crate::jer::encode(&set).unwrap();
let trimmed = actual_encoding
.trim_start_matches('[')
.trim_end_matches(']');
let sum = trimmed
.split(',')
.map(|num_str| num_str.trim().parse::<i32>())
.sum::<Result<i32, _>>()
.unwrap();
assert_eq!(sum, 15);
let decoded_value: SetOf<_> = crate::jer::decode(&actual_encoding).unwrap();
assert_eq!(set, decoded_value);
round_trip_jer!(SetOf<bool>, SetOf::from_vec(alloc::vec![]), "[]");
}
#[test]
fn seqence() {
round_trip_jer!(
TestTypeA,
TestTypeA {
juice: 0.into(),
wine: Inner::Wine(4),
grappa: [true, false].iter().collect::<BitString>()
},
r#"{"grappa":{"length":2,"value":"80"},"juice":0,"wine":{"Wine":4}}"#
);
round_trip_jer!(
Very,
Very {
a: Some(Nested {
very: Some(Struct { strct: None }),
nested: Some(false)
})
},
r#"{"a":{"nested":false,"very":{}}}"#
);
}
#[test]
fn null_optional_present_vs_absent() {
#[derive(AsnType, Decode, Encode, Debug, PartialEq)]
#[rasn(automatic_tags)]
#[rasn(crate_root = "crate")]
struct WithNullOpt {
name: Utf8String,
flag: Option<()>,
}
round_trip_jer!(
WithNullOpt,
WithNullOpt {
name: "test".into(),
flag: Some(())
},
r#"{"flag":null,"name":"test"}"#
);
round_trip_jer!(
WithNullOpt,
WithNullOpt {
name: "test".into(),
flag: None
},
r#"{"name":"test"}"#
);
}
#[test]
fn with_identifier_annotation() {
round_trip_jer!(
Renamed,
Renamed {
very: 1.into(),
renamed: Some(true),
},
r#"{"re_named":true,"so-very":1}"#
);
round_trip_jer!(Renumed, Renumed::Test1("hel".into()), r#"{"test-1":"hel"}"#);
}
}