#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameKind {
Mapping,
Sequence,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScalarPosition {
Key,
Value,
Outside,
}
pub struct Frame<R, K, P> {
kind: FrameKind,
role: R,
awaiting_key: bool,
pending_key: K,
is_complex_key: bool,
pub payload: P,
}
impl<R, K, P> Frame<R, K, P> {
#[must_use]
pub const fn kind(&self) -> FrameKind {
self.kind
}
#[must_use]
pub const fn role(&self) -> &R {
&self.role
}
#[must_use]
pub const fn pending_key(&self) -> &K {
&self.pending_key
}
}
pub struct FrameStack<R, K, P> {
frames: Vec<Frame<R, K, P>>,
}
impl<R, K, P> Default for FrameStack<R, K, P>
where
K: Clone + Default,
{
fn default() -> Self {
Self::new()
}
}
impl<R, K, P> FrameStack<R, K, P>
where
K: Clone + Default,
{
#[must_use]
pub const fn new() -> Self {
Self { frames: Vec::new() }
}
#[must_use]
pub fn depth(&self) -> usize {
self.frames.len()
}
#[must_use]
pub fn top(&self) -> Option<&Frame<R, K, P>> {
self.frames.last()
}
#[must_use]
pub fn top_mut(&mut self) -> Option<&mut Frame<R, K, P>> {
self.frames.last_mut()
}
#[must_use]
pub fn top_role_or(&self, default: R) -> R
where
R: Copy,
{
self.top().map_or(default, |frame| *frame.role())
}
pub fn clear(&mut self) {
self.frames.clear();
}
#[must_use]
pub fn scalar_position(&self) -> ScalarPosition {
match self.frames.last() {
None => ScalarPosition::Outside,
Some(frame) if frame.kind == FrameKind::Sequence => ScalarPosition::Outside,
Some(frame) if frame.awaiting_key => ScalarPosition::Key,
Some(_) => ScalarPosition::Value,
}
}
#[must_use]
pub fn is_complex_key_position(&self) -> bool {
self.frames
.last()
.is_some_and(|frame| frame.kind == FrameKind::Mapping && frame.awaiting_key)
}
pub fn observe_key(&mut self, key: K)
where
K: PartialEq,
{
if let Some(top) = self.frames.last_mut() {
debug_assert!(
!top.awaiting_key || top.pending_key == K::default(),
"observe_key: pending_key was not at its default while awaiting_key was \
true — the pending_key/awaiting_key invariant this module relies on \
throughout has been violated"
);
top.pending_key = key;
top.awaiting_key = false;
}
}
pub fn consume_value(&mut self) {
if let Some(top) = self.frames.last_mut() {
top.awaiting_key = true;
top.pending_key = K::default();
}
}
pub fn push(&mut self, kind: FrameKind, role: R, payload: P) {
let is_complex_key = self.is_complex_key_position();
if !is_complex_key {
self.consume_value();
}
self.frames.push(Frame {
kind,
role,
awaiting_key: true,
pending_key: K::default(),
is_complex_key,
payload,
});
}
pub fn pop(&mut self) -> Option<Frame<R, K, P>> {
let frame = self.frames.pop()?;
if frame.is_complex_key {
if let Some(parent) = self.frames.last_mut() {
parent.awaiting_key = false;
parent.pending_key = K::default();
}
} else {
self.consume_value();
}
Some(frame)
}
}
#[cfg(test)]
mod tests {
use super::{FrameKind, FrameStack, ScalarPosition};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum Key {
#[default]
None,
Name,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Role {
Root,
Irrelevant,
}
#[test]
fn test_key_value_toggling() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, ());
assert_eq!(stack.scalar_position(), ScalarPosition::Key);
stack.observe_key(Key::Name);
assert_eq!(stack.scalar_position(), ScalarPosition::Value);
assert_eq!(*stack.top().unwrap().pending_key(), Key::Name);
stack.consume_value();
assert_eq!(stack.scalar_position(), ScalarPosition::Key);
assert_eq!(*stack.top().unwrap().pending_key(), Key::None);
}
#[test]
fn test_sequence_parent_scalar_position_is_outside() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Sequence, Role::Irrelevant, ());
assert_eq!(stack.scalar_position(), ScalarPosition::Outside);
stack.observe_key(Key::Name);
assert_eq!(*stack.top().unwrap().pending_key(), Key::Name);
assert_eq!(stack.scalar_position(), ScalarPosition::Outside);
}
#[test]
fn test_empty_stack_scalar_position_is_outside() {
let stack: FrameStack<Role, Key, ()> = FrameStack::new();
assert_eq!(stack.scalar_position(), ScalarPosition::Outside);
assert!(stack.top().is_none());
}
#[test]
fn test_alias_in_key_position_toggles_same_as_a_scalar_key() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, ());
assert_eq!(stack.scalar_position(), ScalarPosition::Key);
stack.observe_key(Key::Name);
assert_eq!(stack.scalar_position(), ScalarPosition::Value);
stack.consume_value();
assert_eq!(stack.scalar_position(), ScalarPosition::Key);
}
#[test]
fn test_complex_key_subtree_does_not_disturb_parent_then_flips_to_awaiting_value() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, ());
assert!(stack.is_complex_key_position());
stack.push(FrameKind::Mapping, Role::Irrelevant, ());
assert_eq!(stack.depth(), 2);
let closed = stack.pop().unwrap();
assert_eq!(closed.kind(), FrameKind::Mapping);
assert_eq!(stack.depth(), 1);
assert_eq!(stack.scalar_position(), ScalarPosition::Value);
assert_eq!(*stack.top().unwrap().pending_key(), Key::None);
}
#[test]
fn test_nested_complex_key_sequence_form() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, ());
stack.push(FrameKind::Sequence, Role::Irrelevant, ());
assert_eq!(stack.top().unwrap().kind(), FrameKind::Sequence);
stack.pop();
assert_eq!(stack.scalar_position(), ScalarPosition::Value);
}
#[test]
fn test_ordinary_mapping_value_push_pop_resets_parent_to_awaiting_key() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, ());
stack.observe_key(Key::Name);
assert!(!stack.is_complex_key_position());
stack.push(FrameKind::Mapping, Role::Irrelevant, ());
stack.pop();
assert_eq!(stack.scalar_position(), ScalarPosition::Key);
assert_eq!(*stack.top().unwrap().pending_key(), Key::None);
}
#[test]
fn test_depth_tracks_open_frames() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
assert_eq!(stack.depth(), 0);
stack.push(FrameKind::Mapping, Role::Root, ());
assert_eq!(stack.depth(), 1);
stack.push(FrameKind::Sequence, Role::Irrelevant, ());
assert_eq!(stack.depth(), 2);
stack.pop();
assert_eq!(stack.depth(), 1);
}
#[test]
fn test_clear_drops_every_open_frame() {
let mut stack: FrameStack<Role, Key, ()> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, ());
stack.push(FrameKind::Sequence, Role::Irrelevant, ());
stack.clear();
assert_eq!(stack.depth(), 0);
assert_eq!(stack.scalar_position(), ScalarPosition::Outside);
}
#[test]
fn test_payload_is_directly_mutable_between_key_and_value() {
let mut stack: FrameStack<Role, Key, Vec<u32>> = FrameStack::new();
stack.push(FrameKind::Mapping, Role::Root, Vec::new());
stack.top_mut().unwrap().payload.push(1);
stack.observe_key(Key::Name);
stack.top_mut().unwrap().payload.push(2);
assert_eq!(stack.top().unwrap().payload, vec![1, 2]);
}
}