#![warn(rust_2018_idioms, clippy::pedantic)]
#![warn(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
clippy::panic,
clippy::panic_in_result_fn,
clippy::panicking_unwrap,
clippy::all
)]
#![forbid(unsafe_code)]
#![feature(min_specialization)]
pub(crate) mod tag;
pub mod de;
pub mod ser;
pub mod value;
pub use de::{Deserializer, Error as DeError, VisitorExt};
pub use ser::{Error as SerError, SerializeExt, Serializer};
pub use value::{from_value, to_value, Object, RbArray, RbHash, RbString, Symbol, Userdata, Value};
#[allow(clippy::missing_errors_doc)]
pub fn from_bytes<'de, T>(data: &'de [u8]) -> Result<T, DeError>
where
T: serde::Deserialize<'de>,
{
let mut deserializer = Deserializer::new(data)?;
T::deserialize(&mut deserializer)
}
pub fn to_bytes<T>(data: T) -> Result<Vec<u8>, SerError>
where
T: serde::Serialize,
{
let mut serializer = Serializer::new();
data.serialize(&mut serializer)?;
Ok(serializer.output)
}
#[cfg(test)]
mod ints {
#[test]
fn deserialize() {
let bytes = &[0x04, 0x08, 0x69, 0x19];
let int: u8 = crate::from_bytes(bytes).unwrap();
assert_eq!(int, 20);
}
#[test]
fn round_trip() {
let int = 123;
let bytes = crate::to_bytes(int).unwrap();
let int2 = crate::from_bytes(&bytes).unwrap();
assert_eq!(int, int2);
}
#[test]
fn round_trip_value() {
let value = crate::Value::Integer(123);
let bytes = crate::to_bytes(&value).unwrap();
let value2: crate::Value = crate::from_bytes(&bytes).unwrap();
assert_eq!(value, value2);
}
#[test]
fn negatives() {
let bytes = &[0x04, 0x08, 0x69, 0xfd, 0x1d, 0xf0, 0xfc];
let int: i32 = crate::from_bytes(bytes).unwrap();
assert_eq!(int, -200_675);
}
}
#[cfg(test)]
mod strings {
#[test]
fn deserialize() {
let bytes = &[
0x04, 0x08, 0x49, 0x22, 0x11, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x65,
0x72, 0x65, 0x21, 0x06, 0x3a, 0x06, 0x45, 0x54,
];
let str: &str = crate::from_bytes(bytes).unwrap();
assert_eq!(str, "hello there!");
}
#[test]
fn round_trip() {
let str = "round trip!!";
let bytes = crate::to_bytes(str).unwrap();
let str2: &str = crate::from_bytes(&bytes).unwrap();
assert_eq!(str, str2);
}
#[test]
fn weird_encoding() {
let bytes = &[
0x04, 0x08, 0x49, 0x22, 0x11, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x65,
0x72, 0x65, 0x21, 0x06, 0x3a, 0x0d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67,
0x22, 0x09, 0x42, 0x69, 0x67, 0x35,
];
let str: crate::RbString = crate::from_bytes(bytes).unwrap();
assert_eq!(
str.encoding().unwrap().as_string().unwrap().data, "Big5".as_bytes()
);
}
#[test]
fn weird_encoding_round_trip() {
let bytes: &[_] = &[
0x04, 0x08, 0x49, 0x22, 0x11, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x65,
0x72, 0x65, 0x21, 0x06, 0x3a, 0x0d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67,
0x22, 0x09, 0x42, 0x69, 0x67, 0x35,
];
let str: crate::RbString = crate::from_bytes(bytes).unwrap();
let bytes2 = crate::to_bytes(&str).unwrap();
assert_eq!(bytes, bytes2);
}
}
#[cfg(test)]
mod floats {
#[test]
fn deserialize() {
let bytes = &[0x04, 0x08, 0x66, 0x07, 0x31, 0x35];
let float: f64 = crate::from_bytes(bytes).unwrap();
assert!((float - 15.0).abs() < f64::EPSILON);
}
#[test]
fn round_trip() {
let float = 20870.15;
let bytes = crate::to_bytes(float).unwrap();
let float2: f64 = crate::from_bytes(&bytes).unwrap();
assert!((float - float2).abs() < f64::EPSILON);
}
#[test]
fn nan() {
let bytes = &[0x04, 0x08, 0x66, 0x08, 0x6e, 0x61, 0x6e];
let float: f64 = crate::from_bytes(bytes).unwrap();
assert!(float.is_nan());
}
#[test]
fn round_trip_nan() {
let float = f64::NAN;
let bytes = crate::to_bytes(float).unwrap();
let float2: f64 = crate::from_bytes(&bytes).unwrap();
assert!(float.is_nan());
assert_eq!(
bytemuck::cast::<_, u64>(float),
bytemuck::cast::<_, u64>(float2)
);
}
}
#[cfg(test)]
mod arrays {
#[test]
fn deserialize() {
let bytes = &[
0x04, 0x08, 0x5b, 0x0a, 0x69, 0x00, 0x69, 0x06, 0x69, 0x07, 0x69, 0x08, 0x69, 0x09,
];
let ary: Vec<u8> = crate::from_bytes(bytes).unwrap();
assert_eq!(ary, vec![0, 1, 2, 3, 4]);
}
#[test]
fn round_trip() {
let ary = vec!["hi!", "goodbye!", "pain"];
let bytes = crate::to_bytes(&ary).unwrap();
let ary2: Vec<&str> = crate::from_bytes(&bytes).unwrap();
assert_eq!(ary, ary2);
}
}
#[cfg(test)]
mod structs {
#[test]
fn deserialize_borrowed() {
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)]
struct Test<'d> {
field1: bool,
field2: &'d str,
}
let bytes = &[
0x04, 0x08, 0x6f, 0x3a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x07, 0x3a, 0x0c, 0x40, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x31, 0x54, 0x3a, 0x0c, 0x40, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x32, 0x49, 0x22, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x72,
0x65, 0x06, 0x3a, 0x06, 0x45, 0x54,
];
let obj: Test<'_> = crate::from_bytes(bytes).unwrap();
assert_eq!(
obj,
Test {
field1: true,
field2: "hello there"
}
);
}
#[test]
fn userdata() {
#[derive(serde::Deserialize, Debug, PartialEq, Eq)]
#[serde(from = "crate::Userdata")]
struct MyUserData {
field: [char; 4],
}
impl From<crate::Userdata> for MyUserData {
fn from(value: crate::Userdata) -> Self {
assert_eq!(value.class, "MyUserData");
let field = std::array::from_fn(|i| value.data[i] as char);
Self { field }
}
}
let bytes = &[
0x04, 0x08, 0x75, 0x3a, 0x0f, 0x4d, 0x79, 0x55, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74,
0x61, 0x09, 0x61, 0x62, 0x63, 0x64,
];
let data: MyUserData = crate::from_bytes(bytes).unwrap();
assert_eq!(
data,
MyUserData {
field: ['a', 'b', 'c', 'd']
}
);
}
}
#[cfg(test)]
mod misc {
#[test]
fn symbol() {
let sym = crate::Symbol::from("symbol");
let bytes = crate::to_bytes(&sym).unwrap();
let sym2: crate::Symbol = crate::from_bytes(&bytes).unwrap();
assert_eq!(sym, sym2);
}
#[test]
fn symlink() {
let bytes = &[
0x04, 0x08, 0x5b, 0x0a, 0x3a, 0x09, 0x74, 0x65, 0x73, 0x74, 0x3b, 0x00, 0x3b, 0x00,
0x3b, 0x00, 0x3b, 0x00,
];
let symbols: Vec<&str> = crate::from_bytes(bytes).unwrap();
for sym in symbols.windows(2) {
assert_eq!(sym[0].as_ptr(), sym[1].as_ptr());
}
}
}
#[cfg(test)]
mod value_test {
#[test]
fn untyped_object() {
let bytes = &[
0x04, 0x08, 0x6f, 0x3a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x07, 0x3a, 0x0c, 0x40, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x31, 0x54, 0x3a, 0x0c, 0x40, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x32, 0x49, 0x22, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x72,
0x65, 0x06, 0x3a, 0x06, 0x45, 0x54,
];
let obj: crate::Value = crate::from_bytes(bytes).unwrap();
let obj = obj.into_object().unwrap();
assert_eq!(obj.class, "Test");
assert_eq!(obj.fields["field1"], true);
}
#[test]
fn untyped_to_borrowed() {
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)]
struct Test<'d> {
field1: bool,
field2: &'d str,
}
let bytes = &[
0x04, 0x08, 0x6f, 0x3a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x07, 0x3a, 0x0c, 0x40, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x31, 0x54, 0x3a, 0x0c, 0x40, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x32, 0x49, 0x22, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x72,
0x65, 0x06, 0x3a, 0x06, 0x45, 0x54,
];
let obj: crate::Value = crate::from_bytes(bytes).unwrap();
let test: Test<'_> = serde::Deserialize::deserialize(&obj).unwrap();
assert_eq!(
test,
Test {
field1: true,
field2: "hello there"
}
);
}
}