use capnp::message::TypedReader;
use capnp::serialize::{BufferSegments, OwnedSegments};
use crate::capnp::jeff_capnp;
use crate::reader::{Module, ReadJeff};
use crate::JeffError;
#[derive(Debug, Clone)]
pub struct Jeff<'a> {
module: JeffCow<'a>,
}
enum JeffCow<'a> {
Borrowed(TypedReader<BufferSegments<&'a [u8]>, jeff_capnp::module::Owned>),
Owned(TypedReader<OwnedSegments, jeff_capnp::module::Owned>),
}
impl<'a> Jeff<'a> {
pub const VERSION: semver::Version = crate::SCHEMA_VERSION;
pub const MIN_COMPATIBLE_VERSION: semver::Version = semver::Version::new(0, 3, 0);
pub const MAX_COMPATIBLE_VERSION: semver::Version = semver::Version::new(0, 3, u64::MAX);
pub fn read_slice(slice: &mut &'a [u8]) -> Result<Self, JeffError> {
let reader = capnp::serialize::read_message_from_flat_slice(
slice,
capnp::message::ReaderOptions::new(),
)?;
let module = reader.into_typed::<jeff_capnp::module::Owned>();
module.get()?;
let slf = Self {
module: JeffCow::Borrowed(module),
};
slf.check_version()?;
Ok(slf)
}
pub fn read(reader: impl std::io::Read) -> Result<Self, JeffError> {
let reader = capnp::serialize::read_message(reader, capnp::message::ReaderOptions::new())?;
let module = reader.into_typed::<jeff_capnp::module::Owned>();
module.get()?;
let slf = Self {
module: JeffCow::Owned(module),
};
slf.check_version()?;
Ok(slf)
}
fn check_version(&self) -> Result<(), JeffError> {
let version = self.module().version();
if version < Self::MIN_COMPATIBLE_VERSION {
return Err(JeffError::VersionTooOld {
v: version,
min: Self::MIN_COMPATIBLE_VERSION.to_string(),
});
}
if version > Self::MAX_COMPATIBLE_VERSION {
let x_if_max = |v: u64| match v {
u64::MAX => "x".to_string(),
_ => v.to_string(),
};
let max = format!(
"{}.{}.{}",
x_if_max(Self::MAX_COMPATIBLE_VERSION.major),
x_if_max(Self::MAX_COMPATIBLE_VERSION.minor),
x_if_max(Self::MAX_COMPATIBLE_VERSION.patch)
);
return Err(JeffError::VersionTooNew { v: version, max });
}
Ok(())
}
}
impl ReadJeff for Jeff<'_> {
fn module(&self) -> Module<'_> {
Module::read_capnp(self.module.module())
}
}
impl JeffCow<'_> {
pub fn module(&self) -> jeff_capnp::module::Reader<'_> {
match self {
Self::Borrowed(module) => module.get().expect("Root type should be correct"),
Self::Owned(module) => module.get().expect("Root type should be correct"),
}
}
}
impl Clone for JeffCow<'_> {
fn clone(&self) -> Self {
todo!()
}
}
impl std::fmt::Debug for JeffCow<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Borrowed(_) => f.debug_tuple("JeffCow::Borrowed").finish_non_exhaustive(),
Self::Owned(_) => f.debug_tuple("JeffCow::Owned").finish_non_exhaustive(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test::entangled_qs;
use rstest::rstest;
#[rstest]
fn simple_jeff(entangled_qs: Jeff<'static>) {
entangled_qs.check_version().unwrap();
}
}