use std::collections::HashMap;
use vitaminc_protected::{OpaqueDebug, Protected};
use crate::frame::{to_frame_at, FrameWriteError, MAX_DEPTH};
use crate::plaintext::{Encrypt, Error, Logical, Path, Physical, Step};
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum Unencryptable {
#[error("duplicate key")]
DuplicateKey,
#[error("sequence has nothing sealed")]
SequenceHasNothingSealed,
#[error("map has nothing sealed")]
MapHasNothingSealed,
#[error("value is not valid for its logical type")]
InvalidValue,
#[error("value cannot be written as a leaf")]
Unwritable,
#[error("passthrough subtree cannot be framed")]
UnframablePassthrough,
#[error("a child sink was dropped without a value")]
DroppedChild,
#[error("value nests deeper than {MAX_DEPTH} levels")]
TooDeep,
}
#[derive(Clone, OpaqueDebug, thiserror::Error)]
#[error("{reason}")]
pub struct Refusal {
#[non_sensitive]
reason: Unencryptable,
#[non_sensitive]
path: Path,
}
impl Refusal {
pub fn reason(&self) -> Unencryptable {
self.reason
}
pub fn path(&self) -> &Path {
&self.path
}
}
pub trait Locate {
fn locate(&self, steps: &mut Vec<Step>);
}
pub struct Root;
impl Locate for Root {
fn locate(&self, _: &mut Vec<Step>) {}
}
pub(crate) fn refuse(at: &dyn Locate, reason: Unencryptable) -> Refusal {
refuse_under(at, None, reason)
}
fn refuse_under(at: &dyn Locate, step: Option<Step>, reason: Unencryptable) -> Refusal {
let mut steps = Vec::new();
at.locate(&mut steps);
steps.extend(step);
Refusal {
reason,
path: Path::new(steps),
}
}
fn depth(depth: usize, at: &dyn Locate) -> Result<(), Refusal> {
if depth > MAX_DEPTH {
return Err(refuse(at, Unencryptable::TooDeep));
}
Ok(())
}
pub fn scalar(
depth: usize,
ty: Option<Logical>,
value: &Physical<'_>,
at: &dyn Locate,
) -> Result<(), Refusal> {
self::depth(depth, at)?;
if ty.is_some_and(|ty| !ty.admits(value)) {
return Err(refuse(at, Unencryptable::InvalidValue));
}
Ok(())
}
pub fn container(depth: usize, at: &dyn Locate) -> Result<(), Refusal> {
self::depth(depth, at)
}
pub fn frame_passthrough<P, E>(
value: P,
depth: usize,
at: &dyn Locate,
refused: impl FnOnce(Refusal) -> E,
) -> Result<Protected<Vec<u8>>, E>
where
P: Encrypt,
E: Error,
{
if let Err(refusal) = self::depth(depth, at) {
return Err(refused(refusal));
}
to_frame_at(value, depth + 1).map_err(|e| match e {
FrameWriteError::Source(e) => E::source(e),
FrameWriteError::TooDeep => refused(refuse(at, Unencryptable::TooDeep)),
_ => refused(refuse(at, Unencryptable::UnframablePassthrough)),
})
}
#[derive(Default)]
struct Children {
handed: usize,
settled: usize,
sealed: bool,
}
impl Children {
fn settled(&self, at: &dyn Locate) -> Result<(), Refusal> {
if self.settled != self.handed {
return Err(refuse(at, Unencryptable::DroppedChild));
}
Ok(())
}
fn hand(&mut self) -> usize {
let position = self.handed;
self.handed += 1;
position
}
fn end(&self, at: &dyn Locate, nothing_sealed: Unencryptable) -> Result<(), Refusal> {
self.settled(at)?;
if self.handed > 0 && !self.sealed {
return Err(refuse(at, nothing_sealed));
}
Ok(())
}
}
#[derive(Default)]
pub struct SeqRules(Children);
impl SeqRules {
pub fn new() -> Self {
Self::default()
}
pub fn element(&mut self, at: &dyn Locate) -> Result<Settle<'_>, Refusal> {
self.0.settled(at)?;
Ok(Settle {
step: Under::Element(self.0.hand()),
children: &mut self.0,
})
}
pub fn end(&self, at: &dyn Locate) -> Result<(), Refusal> {
self.0.end(at, Unencryptable::SequenceHasNothingSealed)
}
}
#[derive(Default)]
pub struct MapRules {
keys: HashMap<String, usize>,
children: Children,
}
impl MapRules {
pub fn new() -> Self {
Self::default()
}
pub fn entry(&mut self, key: &str, at: &dyn Locate) -> Result<Settle<'_>, Refusal> {
let Self { keys, children } = self;
children.settled(at)?;
if keys.contains_key(key) {
let step = Step::Key(key.to_owned());
return Err(refuse_under(at, Some(step), Unencryptable::DuplicateKey));
}
keys.insert(key.to_owned(), children.hand());
Ok(Settle {
step: Under::Entry(keys.get_key_value(key).map(|(key, _)| key.as_str())),
children,
})
}
pub fn end(&self, at: &dyn Locate) -> Result<(), Refusal> {
self.children.end(at, Unencryptable::MapHasNothingSealed)
}
pub(crate) fn into_keys(self) -> impl Iterator<Item = (String, usize)> {
self.keys.into_iter()
}
}
enum Under<'r> {
Element(usize),
Entry(Option<&'r str>),
}
pub struct Settle<'r> {
step: Under<'r>,
children: &'r mut Children,
}
impl Settle<'_> {
pub fn sealed(self) {
self.children.settled += 1;
self.children.sealed = true;
}
pub fn passthrough(self) {
self.children.settled += 1;
}
pub(crate) fn locate_under(&self, parent: &dyn Locate, steps: &mut Vec<Step>) {
parent.locate(steps);
match self.step {
Under::Element(index) => steps.push(Step::Index(index)),
Under::Entry(key) => steps.extend(key.map(|key| Step::Key(key.to_owned()))),
}
}
}