use std::io::BufReader;
use crate::{
Number, Version,
load::{Loadable, Result, error},
save,
};
pub(crate) trait LoadContext {
fn value(&self) -> Result<&save::Value<'_>>;
fn read(&self, key: &str) -> Result<Reader<'_>>;
}
pub(crate) trait ReaderInner: std::io::Read + std::io::Seek {}
impl<T> ReaderInner for T where T: std::io::Read + std::io::Seek {}
pub struct Reader<'a> {
io: BufReader<Box<dyn ReaderInner + 'a>>,
}
impl<'a> Reader<'a> {
pub(crate) fn new<T>(io: T) -> Self
where
T: ReaderInner + 'a,
{
Self {
io: BufReader::new(Box::new(io)),
}
}
}
impl std::io::Read for Reader<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.io.read(buf)
}
fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
self.io.read_vectored(bufs)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
self.io.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
self.io.read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
self.io.read_exact(buf)
}
}
impl std::io::Seek for Reader<'_> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
self.io.seek(pos)
}
fn rewind(&mut self) -> std::io::Result<()> {
self.io.rewind()
}
fn stream_position(&mut self) -> std::io::Result<u64> {
self.io.stream_position()
}
fn seek_relative(&mut self, offset: i64) -> std::io::Result<()> {
self.io.seek_relative(offset)
}
}
#[derive(Clone)]
pub struct Context<'a> {
inner: &'a dyn LoadContext,
value: &'a save::Value<'a>,
}
impl<'a> Context<'a> {
pub(super) fn new(inner: &'a dyn LoadContext, value: &'a save::Value<'a>) -> Self {
Self { inner, value }
}
fn context(&self) -> &'a dyn LoadContext {
self.inner
}
pub fn load<T>(&self) -> Result<T>
where
T: Loadable<'a>,
{
T::load(self.clone())
}
pub fn as_object(&self) -> Option<Object<'a>> {
match self.value {
save::Value::Object(versioned) => {
let object = Object {
inner: self.inner,
record: versioned.record(),
version: versioned.version(),
};
Some(object)
}
_ => None,
}
}
pub fn as_str(&self) -> Option<&'a str> {
match self.value {
save::Value::String(s) => Some(s),
_ => None,
}
}
pub fn as_array(&self) -> Option<Array<'a>> {
match self.value {
save::Value::Array(array) => Some(Array::new(self.context(), array)),
_ => None,
}
}
pub fn as_number(&self) -> Option<Number> {
match self.value {
save::Value::Number(number) => Some(*number),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self.value {
save::Value::Bool(value) => Some(*value),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self.value, save::Value::Null)
}
pub(crate) fn as_handle(&self) -> Option<&save::Handle> {
match self.value {
save::Value::Handle(handle) => Some(handle),
_ => None,
}
}
}
pub struct Object<'a> {
inner: &'a dyn LoadContext,
record: &'a save::Record<'a>,
version: Version,
}
impl<'a> Object<'a> {
pub fn version(&self) -> Version {
self.version
}
pub fn keys(&self) -> save::Keys<'_, 'a> {
self.record.keys()
}
pub fn len(&self) -> usize {
self.record.len()
}
pub fn is_empty(&self) -> bool {
self.record.is_empty()
}
pub fn single_key(&self) -> Result<&str> {
let mut keys = self.record.keys();
let Some(first) = keys.next() else {
return Err(error::Kind::TypeMismatch.into());
};
if keys.next().is_some() {
return Err(error::Kind::TypeMismatch.into());
}
Ok(first)
}
pub fn child(&self, key: &str) -> Result<Context<'a>> {
match self.record.get(key) {
Some(value) => Ok(Context::new(self.context(), value)),
None => Err(error::Kind::MissingField.into()),
}
}
pub fn field<T>(&self, key: &str) -> Result<T>
where
T: Loadable<'a>,
{
match self.record.get(key) {
Some(value) => T::load(Context::new(self.context(), value)),
None => Err((error::Kind::MissingField).into()),
}
}
pub fn read(&self, handle: &save::Handle) -> Result<Reader<'_>> {
self.inner.read(handle.as_str())
}
fn context(&self) -> &'a dyn LoadContext {
self.inner
}
}
pub struct Array<'a> {
inner: &'a dyn LoadContext,
array: &'a [save::Value<'a>],
}
impl<'a> Array<'a> {
fn new(inner: &'a dyn LoadContext, array: &'a [save::Value<'a>]) -> Self {
Self { inner, array }
}
pub fn len(&self) -> usize {
self.array.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> Iter<'a> {
Iter::new(self.context(), self.array.iter())
}
fn context(&self) -> &'a dyn LoadContext {
self.inner
}
}
pub struct Iter<'a> {
inner: &'a dyn LoadContext,
iter: std::slice::Iter<'a, save::Value<'a>>,
}
impl<'a> Iter<'a> {
fn new(inner: &'a dyn LoadContext, iter: std::slice::Iter<'a, save::Value<'a>>) -> Self {
Self { inner, iter }
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Context<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|value| Context::new(self.inner, value))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl ExactSizeIterator for Iter<'_> {}
#[cfg(test)]
mod tests {
use super::*;
struct TestContext;
impl LoadContext for TestContext {
fn value(&self) -> Result<&save::Value<'_>> {
unimplemented!("not exercised by Object accessor tests")
}
fn read(&self, _key: &str) -> Result<Reader<'_>> {
unimplemented!("not exercised by Object accessor tests")
}
}
#[test]
fn object_reports_populated_keys() {
let mut record = save::Record::empty();
record.insert("alpha", save::Value::Null).unwrap();
record.insert("beta", save::Value::Bool(true)).unwrap();
let value = record.into_value(Version::new(1, 0));
let inner = TestContext;
let object = Context::new(&inner, &value)
.as_object()
.expect("a versioned object value must yield an Object");
assert_eq!(object.len(), 2);
assert!(!object.is_empty());
let mut keys: Vec<&str> = object.keys().collect();
keys.sort_unstable();
assert_eq!(keys, ["alpha", "beta"]);
}
#[test]
fn object_reports_empty_record() {
let value = save::Record::empty().into_value(Version::new(1, 0));
let inner = TestContext;
let object = Context::new(&inner, &value)
.as_object()
.expect("a versioned object value must yield an Object");
assert_eq!(object.len(), 0);
assert!(object.is_empty());
assert_eq!(object.keys().count(), 0);
}
}