#[derive(Debug, Clone)]
pub struct ParsingContext {
pub indent_level: usize,
pub in_flow: bool,
pub after_newline: bool,
pub collection_type: CollectionType,
#[allow(dead_code)]
pub parent: Option<Box<ParsingContext>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CollectionType {
None,
BlockSequence,
#[allow(dead_code)]
BlockMapping,
#[allow(dead_code)]
FlowSequence,
#[allow(dead_code)]
FlowMapping,
}
impl ParsingContext {
pub fn new(indent_level: usize) -> Self {
Self {
indent_level,
in_flow: false,
after_newline: false,
collection_type: CollectionType::None,
parent: None,
}
}
pub fn child_block_context(
&self,
indent_level: usize,
collection_type: CollectionType,
) -> Self {
Self {
indent_level,
in_flow: false,
after_newline: self.after_newline,
collection_type,
parent: Some(Box::new(self.clone())),
}
}
#[allow(dead_code)]
pub fn child_flow_context(&self, collection_type: CollectionType) -> Self {
Self {
indent_level: self.indent_level,
in_flow: true,
after_newline: false, collection_type,
parent: Some(Box::new(self.clone())),
}
}
#[allow(dead_code)]
pub fn mark_newline_consumed(&mut self) {
self.after_newline = true;
}
#[allow(dead_code)]
pub fn mark_content_found(&mut self) {
self.after_newline = false;
}
pub fn should_validate_tab_indentation(&self) -> bool {
!self.in_flow && self.after_newline
}
#[allow(dead_code)]
pub fn parent_context(&self) -> Option<&ParsingContext> {
self.parent.as_ref().map(|b| b.as_ref())
}
#[allow(dead_code)]
pub fn is_valid_child_indent(&self, current_indent: usize) -> bool {
if let Some(parent) = self.parent_context() {
current_indent > parent.indent_level
} else {
true }
}
}
impl Default for ParsingContext {
fn default() -> Self {
Self::new(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_context() {
let ctx = ParsingContext::new(0);
assert_eq!(ctx.indent_level, 0);
assert!(!ctx.in_flow);
assert!(!ctx.after_newline);
assert_eq!(ctx.collection_type, CollectionType::None);
assert!(ctx.parent.is_none());
}
#[test]
fn test_child_block_context() {
let root = ParsingContext::new(0);
let child = root.child_block_context(2, CollectionType::BlockSequence);
assert_eq!(child.indent_level, 2);
assert!(!child.in_flow);
assert_eq!(child.collection_type, CollectionType::BlockSequence);
assert!(child.parent.is_some());
}
#[test]
fn test_child_flow_context() {
let root = ParsingContext::new(0);
let flow = root.child_flow_context(CollectionType::FlowSequence);
assert!(flow.in_flow);
assert_eq!(flow.collection_type, CollectionType::FlowSequence);
assert!(flow.parent.is_some());
}
#[test]
fn test_newline_marking() {
let mut ctx = ParsingContext::new(0);
assert!(!ctx.after_newline);
ctx.mark_newline_consumed();
assert!(ctx.after_newline);
assert!(ctx.should_validate_tab_indentation());
ctx.mark_content_found();
assert!(!ctx.after_newline);
assert!(!ctx.should_validate_tab_indentation());
}
#[test]
fn test_tab_validation_in_flow() {
let root = ParsingContext::new(0);
let mut flow = root.child_flow_context(CollectionType::FlowSequence);
flow.mark_newline_consumed();
assert!(!flow.should_validate_tab_indentation());
}
#[test]
fn test_valid_child_indent() {
let parent = ParsingContext::new(2);
let child = parent.child_block_context(4, CollectionType::BlockMapping);
assert!(child.is_valid_child_indent(6));
assert!(child.is_valid_child_indent(4));
assert!(!child.is_valid_child_indent(2));
assert!(!child.is_valid_child_indent(0));
}
#[test]
fn test_parent_access() {
let root = ParsingContext::new(0);
let child = root.child_block_context(2, CollectionType::BlockSequence);
assert!(root.parent_context().is_none());
assert!(child.parent_context().is_some());
assert_eq!(child.parent_context().unwrap().indent_level, 0);
}
}