#![cfg(target_endian = "little")]
use structio::beve::{self, NumericBytes, Read, ReadAs, Reader, Write, WriteAs, Writer, header};
use structio::{ErrorCode, Options, Same, from_beve, to_beve};
type PResult<T> = Result<T, ErrorCode>;
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[repr(transparent)]
struct Celsius(f64);
unsafe impl NumericBytes for Celsius {
const ELEMENT: u8 = <f64 as NumericBytes>::ELEMENT;
}
struct AsDegrees;
impl<'de> ReadAs<'de, Celsius> for AsDegrees {
fn read<O: Options>(value: &mut Celsius, r: &mut Reader<'de, O>) -> PResult<()> {
value.0.read(r)
}
fn read_bulk<O: Options>(
out: &mut Vec<Celsius>,
n: usize,
elem: u8,
r: &mut Reader<'de, O>,
) -> PResult<bool> {
if elem != <Celsius as NumericBytes>::ELEMENT || cfg!(target_endian = "big") {
return Ok(false);
}
r.read_block(out, n)?;
Ok(true)
}
}
impl WriteAs<Celsius> for AsDegrees {
fn write<O: Options>(value: &Celsius, w: &mut Writer<'_, O>) {
value.0.write(w);
}
const ARRAY: Option<&'static [u8]> = <f64 as Write>::ARRAY;
fn write_payload<O: Options>(items: &[Celsius], w: &mut Writer<'_, O>) {
if cfg!(target_endian = "little") {
w.write_block(items);
} else {
for c in items {
w.raw(&c.0.to_le_bytes());
}
}
}
}
#[derive(Default, Debug, PartialEq)]
struct Reading {
samples: Vec<Celsius>,
}
structio::beve_object!(Reading { samples as Vec<AsDegrees> });
#[derive(Default, Debug, PartialEq)]
struct Plain {
samples: Vec<f64>,
}
structio::beve_object!(Plain { samples });
fn degrees() -> Vec<Celsius> {
vec![Celsius(-40.0), Celsius(0.5), Celsius(21.0), Celsius(100.0)]
}
fn plain() -> Vec<f64> {
vec![-40.0, 0.5, 21.0, 100.0]
}
#[test]
fn an_adapted_foreign_scalar_writes_the_typed_array_the_bare_type_would() {
assert_eq!(
to_beve(&Reading { samples: degrees() }),
to_beve(&Plain { samples: plain() })
);
}
#[test]
fn an_adapted_foreign_scalar_reads_the_typed_array_back() {
let doc = to_beve(&Plain { samples: plain() });
assert_eq!(
from_beve::<Reading>(&doc).unwrap(),
Reading { samples: degrees() }
);
}
struct BlockOnly;
impl<'de> ReadAs<'de, Celsius> for BlockOnly {
fn read<O: Options>(_: &mut Celsius, _: &mut Reader<'de, O>) -> PResult<()> {
Err(ErrorCode::ExpectedNumber)
}
fn read_bulk<O: Options>(
out: &mut Vec<Celsius>,
n: usize,
elem: u8,
r: &mut Reader<'de, O>,
) -> PResult<bool> {
<AsDegrees as ReadAs<'de, Celsius>>::read_bulk(out, n, elem, r)
}
}
impl WriteAs<Celsius> for BlockOnly {
fn write<O: Options>(value: &Celsius, w: &mut Writer<'_, O>) {
<AsDegrees as WriteAs<Celsius>>::write(value, w);
}
}
#[derive(Default, Debug, PartialEq)]
struct Strict {
samples: Vec<Celsius>,
}
structio::beve_object!(Strict { samples as Vec<BlockOnly> });
#[test]
fn the_block_read_is_the_one_that_ran() {
let doc = to_beve(&Plain { samples: plain() });
assert_eq!(
from_beve::<Strict>(&doc).unwrap(),
Strict { samples: degrees() }
);
}
#[test]
fn the_aligned_form_reaches_the_adapter_too() {
let aligned = structio::to_beve_aligned(&Plain { samples: plain() });
assert_ne!(
aligned,
to_beve(&Plain { samples: plain() }),
"not the aligned form, so this asserts nothing"
);
assert_eq!(
from_beve::<Strict>(&aligned).unwrap(),
Strict { samples: degrees() }
);
}
#[test]
fn an_adapter_that_consumes_and_then_declines_is_corrected() {
struct Greedy;
impl<'de> ReadAs<'de, Celsius> for Greedy {
fn read<O: Options>(value: &mut Celsius, r: &mut Reader<'de, O>) -> PResult<()> {
value.0.read(r)
}
fn read_bulk<O: Options>(
out: &mut Vec<Celsius>,
n: usize,
_elem: u8,
r: &mut Reader<'de, O>,
) -> PResult<bool> {
r.read_block(out, n)?;
Ok(false)
}
}
let doc = to_beve(&Plain { samples: plain() });
let mut r = Reader::new(&doc);
r.seek("/samples").unwrap();
let at = r.position();
let mut out: Vec<Celsius> = Vec::new();
assert!(!r.try_bulk_with::<Greedy, _>(&mut out).unwrap());
assert_eq!(r.position(), at, "declining left the cursor moved");
<Vec<Greedy> as ReadAs<'_, Vec<Celsius>>>::read(&mut out, &mut r).unwrap();
assert_eq!(out, degrees());
}
#[test]
fn an_adapter_that_declines_falls_back_to_its_element_path() {
let mut w: Writer = Writer::new();
w.begin_generic_array(plain().len());
for x in plain() {
w.element(&x);
}
let array = w.into_vec();
let mut r = Reader::new(&array);
let mut out: Vec<Celsius> = Vec::new();
assert!(
!r.try_bulk_with::<AsDegrees, _>(&mut out).unwrap(),
"a generic array is not a block"
);
assert_eq!(r.position(), 0, "declining consumed input");
<Vec<AsDegrees> as ReadAs<'_, Vec<Celsius>>>::read(&mut out, &mut r).unwrap();
assert_eq!(out, degrees());
}
#[test]
fn an_adapter_declines_a_block_of_another_element_type() {
#[derive(Default)]
struct Narrow {
samples: Vec<f32>,
}
structio::beve_object!(Narrow { samples });
let doc = to_beve(&Narrow {
samples: vec![-40.0, 0.5],
});
let mut r = Reader::new(&doc);
r.seek("/samples").unwrap();
let mut out: Vec<Celsius> = Vec::new();
assert!(!r.try_bulk_with::<AsDegrees, _>(&mut out).unwrap());
assert_eq!(
from_beve::<Reading>(&doc).unwrap().samples,
[Celsius(-40.0), Celsius(0.5)]
);
}
#[test]
fn same_forwards_the_block_read() {
let doc = to_beve(&plain());
let mut r = Reader::new(&doc);
let mut out: Vec<f64> = Vec::new();
assert!(r.try_bulk_with::<Same, _>(&mut out).unwrap());
assert_eq!(out, plain());
r.finish().expect("the block was not consumed whole");
}
#[test]
fn same_reads_a_block_where_the_bare_type_does_and_declines_where_it_does_not() {
let strings = to_beve(&vec![String::from("a"), String::from("b")]);
let mut r = Reader::new(&strings);
let mut out: Vec<String> = Vec::new();
assert!(
!r.try_bulk_with::<Same, _>(&mut out).unwrap(),
"a string array is not a numeric block"
);
assert_eq!(r.position(), 0);
}
#[test]
fn a_block_written_by_hand_is_the_document_the_crate_writes() {
let items = degrees();
let array = <f64 as Write>::ARRAY.expect("f64 has a typed array");
let mut w: Writer = Writer::new();
w.begin_typed_array(array[0], items.len());
w.write_block(&items);
let doc = w.into_vec();
assert_eq!(doc, to_beve(&plain()));
assert_eq!(
header::element_of(doc[0]),
<Celsius as NumericBytes>::ELEMENT
);
}
#[test]
fn a_block_read_replaces_what_the_vector_held() {
let doc = to_beve(&plain());
let mut out = vec![Celsius(f64::NAN); 32];
let mut r = Reader::new(&doc);
assert!(r.try_bulk_with::<AsDegrees, _>(&mut out).unwrap());
assert_eq!(out, degrees());
}
#[test]
fn a_block_read_that_runs_past_the_input_is_an_error() {
let doc = to_beve(&plain());
let mut r = Reader::new(&doc[..doc.len() - 1]);
let mut out: Vec<Celsius> = Vec::new();
assert_eq!(
r.try_bulk_with::<AsDegrees, _>(&mut out).unwrap_err(),
ErrorCode::UnexpectedEnd
);
}
#[test]
fn a_user_type_can_borrow_a_block_out_of_the_document() {
let doc = structio::to_beve_aligned(&plain());
match beve::slice_ref::<Celsius>(&doc) {
Some(block) => assert_eq!(block, degrees()),
None => assert_eq!(from_beve::<Vec<f64>>(&doc).unwrap(), plain()),
}
}