use crate::ported::lint::markedjson::error::MarkedError;
use crate::ported::lint::markedjson::events::{MappingStartEvent, ScalarEvent, SequenceStartEvent};
use crate::ported::lint::markedjson::nodes::{MappingNode, Mark, Node, ScalarNode, SequenceNode};
use crate::ported::lint::markedjson::resolver::NodeKind;
#[derive(Debug, Clone)]
pub struct ComposerError(pub MarkedError);
impl std::fmt::Display for ComposerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::error::Error for ComposerError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventKind {
StreamStart,
StreamEnd,
DocumentStart,
DocumentEnd,
Alias,
Scalar,
SequenceStart,
SequenceEnd,
MappingStart,
MappingEnd,
}
#[derive(Debug, Clone)]
pub enum AnyEvent {
StreamStart,
StreamEnd,
DocumentStart,
DocumentEnd,
Alias,
Scalar(ScalarEvent),
SequenceStart(SequenceStartEvent),
SequenceEnd,
MappingStart(MappingStartEvent),
MappingEnd,
}
impl AnyEvent {
pub fn kind(&self) -> EventKind {
match self {
AnyEvent::StreamStart => EventKind::StreamStart,
AnyEvent::StreamEnd => EventKind::StreamEnd,
AnyEvent::DocumentStart => EventKind::DocumentStart,
AnyEvent::DocumentEnd => EventKind::DocumentEnd,
AnyEvent::Alias => EventKind::Alias,
AnyEvent::Scalar(_) => EventKind::Scalar,
AnyEvent::SequenceStart(_) => EventKind::SequenceStart,
AnyEvent::SequenceEnd => EventKind::SequenceEnd,
AnyEvent::MappingStart(_) => EventKind::MappingStart,
AnyEvent::MappingEnd => EventKind::MappingEnd,
}
}
}
#[derive(Debug, Clone)]
pub enum ComposedNode {
Scalar(ScalarNode),
Sequence(SequenceNode),
Mapping(MappingNode),
}
impl ComposedNode {
pub fn node(&self) -> &Node {
match self {
ComposedNode::Scalar(s) => &s.node,
ComposedNode::Sequence(s) => &s.collection.node,
ComposedNode::Mapping(m) => &m.collection.node,
}
}
pub fn start_mark(&self) -> Option<Mark> {
self.node().start_mark.clone()
}
}
pub trait ParserBackend {
fn check_event(&mut self, kinds: &[EventKind]) -> bool;
fn get_event(&mut self) -> AnyEvent;
fn descend_resolver(&mut self, parent: Option<&ComposedNode>, index: Option<&ComposedNode>);
fn ascend_resolver(&mut self);
fn resolve(
&mut self,
kind: NodeKind,
value: Option<&serde_json::Value>,
implicit: bool,
) -> String;
}
pub struct Composer;
impl Composer {
pub fn check_node<B: ParserBackend>(backend: &mut B) -> bool {
if backend.check_event(&[EventKind::StreamStart]) {
backend.get_event();
}
!backend.check_event(&[EventKind::StreamEnd])
}
pub fn get_node<B: ParserBackend>(backend: &mut B) -> Option<ComposedNode> {
if !backend.check_event(&[EventKind::StreamEnd]) {
return Some(Self::compose_document(backend));
}
None
}
pub fn get_single_node<B: ParserBackend>(
backend: &mut B,
) -> Result<Option<ComposedNode>, ComposerError> {
backend.get_event();
let document = if !backend.check_event(&[EventKind::StreamEnd]) {
Some(Self::compose_document(backend))
} else {
None
};
if !backend.check_event(&[EventKind::StreamEnd]) {
let event = backend.get_event();
let _ = event;
let _ = document.as_ref();
return Err(ComposerError(MarkedError::new(
Some("expected a single document in the stream"),
None,
Some("but found another document"),
None,
None,
)));
}
backend.get_event();
Ok(document)
}
pub fn compose_document<B: ParserBackend>(backend: &mut B) -> ComposedNode {
backend.get_event();
let node = Self::compose_node(backend, None, None);
backend.get_event();
node
}
pub fn compose_node<B: ParserBackend>(
backend: &mut B,
parent: Option<&ComposedNode>,
index: Option<&ComposedNode>,
) -> ComposedNode {
backend.descend_resolver(parent, index);
let node = if backend.check_event(&[EventKind::Scalar]) {
ComposedNode::Scalar(Self::compose_scalar_node(backend))
} else if backend.check_event(&[EventKind::SequenceStart]) {
ComposedNode::Sequence(Self::compose_sequence_node(backend))
} else if backend.check_event(&[EventKind::MappingStart]) {
ComposedNode::Mapping(Self::compose_mapping_node(backend))
} else {
panic!(
"compose_node: expected Scalar/SequenceStart/MappingStart event, \
caller did not check_node first?"
);
};
backend.ascend_resolver();
node
}
pub fn compose_scalar_node<B: ParserBackend>(backend: &mut B) -> ScalarNode {
let event = backend.get_event();
let scalar = match event {
AnyEvent::Scalar(s) => s,
_ => unreachable!("compose_scalar_node called without ScalarEvent on top"),
};
let mut tag = scalar.tag.clone();
if tag.as_deref().is_none() || tag.as_deref() == Some("!") {
tag = Some(backend.resolve(NodeKind::Scalar, Some(&scalar.value), scalar.implicit));
}
ScalarNode::new(
tag.unwrap(),
scalar.value,
scalar.node.event.start_mark,
scalar.node.event.end_mark,
scalar.style,
)
}
pub fn compose_sequence_node<B: ParserBackend>(backend: &mut B) -> SequenceNode {
let start = backend.get_event();
let seq = match start {
AnyEvent::SequenceStart(s) => s,
_ => unreachable!("compose_sequence_node called without SequenceStartEvent"),
};
let mut tag = seq.collection.tag.clone();
if tag.as_deref().is_none() || tag.as_deref() == Some("!") {
tag = Some(backend.resolve(NodeKind::Sequence, None, seq.collection.implicit));
}
let mut node = SequenceNode::new(
tag.unwrap(),
serde_json::Value::Array(Vec::new()),
seq.collection.node.event.start_mark,
None,
seq.collection.flow_style,
);
let mut elements: Vec<ComposedNode> = Vec::new();
let mut index_counter: usize = 0;
while !backend.check_event(&[EventKind::SequenceEnd]) {
let _ = index_counter;
let child = Self::compose_node(backend, None, None);
elements.push(child);
index_counter += 1;
}
let end_event = backend.get_event();
let _ = end_event;
let arr: Vec<serde_json::Value> = elements.iter().map(|c| c.node().value.clone()).collect();
node.collection.node.value = serde_json::Value::Array(arr);
node
}
pub fn compose_mapping_node<B: ParserBackend>(backend: &mut B) -> MappingNode {
let start = backend.get_event();
let mapping = match start {
AnyEvent::MappingStart(m) => m,
_ => unreachable!("compose_mapping_node called without MappingStartEvent"),
};
let mut tag = mapping.collection.tag.clone();
if tag.as_deref().is_none() || tag.as_deref() == Some("!") {
tag = Some(backend.resolve(NodeKind::Mapping, None, mapping.collection.implicit));
}
let mut node = MappingNode::new(
tag.unwrap(),
serde_json::Value::Object(serde_json::Map::new()),
mapping.collection.node.event.start_mark,
None,
mapping.collection.flow_style,
);
let mut pairs: Vec<(ComposedNode, ComposedNode)> = Vec::new();
while !backend.check_event(&[EventKind::MappingEnd]) {
let key = Self::compose_node(backend, None, None);
let value = Self::compose_node(backend, None, None);
pairs.push((key, value));
}
let end_event = backend.get_event();
let _ = end_event;
let mut obj = serde_json::Map::new();
for (k, v) in pairs {
let key_str = match k.node().value.clone() {
serde_json::Value::String(s) => s,
other => other.to_string(),
};
obj.insert(key_str, v.node().value.clone());
}
node.collection.node.value = serde_json::Value::Object(obj);
node
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
struct FakeBackend {
events: std::collections::VecDeque<AnyEvent>,
}
impl FakeBackend {
fn new(events: Vec<AnyEvent>) -> Self {
Self {
events: events.into(),
}
}
}
impl ParserBackend for FakeBackend {
fn check_event(&mut self, kinds: &[EventKind]) -> bool {
self.events
.front()
.map(|e| kinds.contains(&e.kind()))
.unwrap_or(false)
}
fn get_event(&mut self) -> AnyEvent {
self.events.pop_front().expect("event stream underflow")
}
fn descend_resolver(
&mut self,
_parent: Option<&ComposedNode>,
_index: Option<&ComposedNode>,
) {
}
fn ascend_resolver(&mut self) {}
fn resolve(
&mut self,
kind: NodeKind,
_value: Option<&serde_json::Value>,
_implicit: bool,
) -> String {
match kind {
NodeKind::Scalar => "tag:yaml.org,2002:str".to_string(),
NodeKind::Sequence => "tag:yaml.org,2002:seq".to_string(),
NodeKind::Mapping => "tag:yaml.org,2002:map".to_string(),
}
}
}
#[test]
fn composer_error_implements_error_traits() {
let me = MarkedError::new(Some("ctx"), None, Some("prob"), None, None);
let e = ComposerError(me);
let _: &dyn std::error::Error = &e;
assert!(e.to_string().contains("ctx"));
}
#[test]
fn check_node_drops_stream_start_and_returns_true_if_doc_remaining() {
let mut b = FakeBackend::new(vec![
AnyEvent::StreamStart,
AnyEvent::DocumentStart,
AnyEvent::Scalar(ScalarEvent::new(true, json!("x"), None, None, None)),
AnyEvent::DocumentEnd,
AnyEvent::StreamEnd,
]);
assert!(Composer::check_node(&mut b));
assert_eq!(b.get_event().kind(), EventKind::DocumentStart);
}
#[test]
fn check_node_returns_false_at_stream_end() {
let mut b = FakeBackend::new(vec![AnyEvent::StreamEnd]);
assert!(!Composer::check_node(&mut b));
}
#[test]
fn compose_document_returns_scalar_node() {
let mut b = FakeBackend::new(vec![
AnyEvent::DocumentStart,
AnyEvent::Scalar(ScalarEvent::new(true, json!("hello"), None, None, None)),
AnyEvent::DocumentEnd,
]);
let n = Composer::compose_document(&mut b);
match n {
ComposedNode::Scalar(s) => {
assert_eq!(s.node.value, json!("hello"));
assert_eq!(s.node.tag, "tag:yaml.org,2002:str");
}
_ => panic!("expected ScalarNode"),
}
}
#[test]
fn compose_node_dispatches_scalar() {
let mut b = FakeBackend::new(vec![AnyEvent::Scalar(ScalarEvent::new(
true,
json!("x"),
None,
None,
None,
))]);
let n = Composer::compose_node(&mut b, None, None);
assert!(matches!(n, ComposedNode::Scalar(_)));
}
#[test]
fn compose_node_dispatches_sequence() {
let mut b = FakeBackend::new(vec![
AnyEvent::SequenceStart(SequenceStartEvent::new(true, None, None, None)),
AnyEvent::Scalar(ScalarEvent::new(true, json!(1), None, None, None)),
AnyEvent::Scalar(ScalarEvent::new(true, json!(2), None, None, None)),
AnyEvent::SequenceEnd,
]);
let n = Composer::compose_node(&mut b, None, None);
match n {
ComposedNode::Sequence(s) => {
let arr = s.collection.node.value.as_array().unwrap();
assert_eq!(arr.len(), 2);
assert_eq!(arr[0], json!(1));
assert_eq!(arr[1], json!(2));
}
_ => panic!("expected SequenceNode"),
}
}
#[test]
fn compose_node_dispatches_mapping() {
let mut b = FakeBackend::new(vec![
AnyEvent::MappingStart(MappingStartEvent::new(true, None, None, None)),
AnyEvent::Scalar(ScalarEvent::new(true, json!("a"), None, None, None)),
AnyEvent::Scalar(ScalarEvent::new(true, json!(1), None, None, None)),
AnyEvent::Scalar(ScalarEvent::new(true, json!("b"), None, None, None)),
AnyEvent::Scalar(ScalarEvent::new(true, json!(2), None, None, None)),
AnyEvent::MappingEnd,
]);
let n = Composer::compose_node(&mut b, None, None);
match n {
ComposedNode::Mapping(m) => {
let obj = m.collection.node.value.as_object().unwrap();
assert_eq!(obj.get("a"), Some(&json!(1)));
assert_eq!(obj.get("b"), Some(&json!(2)));
}
_ => panic!("expected MappingNode"),
}
}
#[test]
fn compose_scalar_node_resolves_tag_when_none() {
let mut b = FakeBackend::new(vec![AnyEvent::Scalar(ScalarEvent::new(
true,
json!(42),
None,
None,
None,
))]);
let s = Composer::compose_scalar_node(&mut b);
assert_eq!(s.node.tag, "tag:yaml.org,2002:str");
}
#[test]
fn get_single_node_errors_on_two_documents() {
let mut b = FakeBackend::new(vec![
AnyEvent::StreamStart,
AnyEvent::DocumentStart,
AnyEvent::Scalar(ScalarEvent::new(true, json!(1), None, None, None)),
AnyEvent::DocumentEnd,
AnyEvent::DocumentStart,
AnyEvent::Scalar(ScalarEvent::new(true, json!(2), None, None, None)),
AnyEvent::DocumentEnd,
AnyEvent::StreamEnd,
]);
let r = Composer::get_single_node(&mut b);
assert!(r.is_err());
}
#[test]
fn get_single_node_returns_single_document() {
let mut b = FakeBackend::new(vec![
AnyEvent::StreamStart,
AnyEvent::DocumentStart,
AnyEvent::Scalar(ScalarEvent::new(true, json!("only"), None, None, None)),
AnyEvent::DocumentEnd,
AnyEvent::StreamEnd,
]);
let r = Composer::get_single_node(&mut b).unwrap();
assert!(r.is_some());
match r.unwrap() {
ComposedNode::Scalar(s) => assert_eq!(s.node.value, json!("only")),
_ => panic!("expected scalar"),
}
}
#[test]
fn get_single_node_returns_none_on_empty_stream() {
let mut b = FakeBackend::new(vec![AnyEvent::StreamStart, AnyEvent::StreamEnd]);
let r = Composer::get_single_node(&mut b).unwrap();
assert!(r.is_none());
}
#[test]
fn any_event_kind_dispatches_correctly() {
assert_eq!(AnyEvent::StreamStart.kind(), EventKind::StreamStart);
assert_eq!(AnyEvent::StreamEnd.kind(), EventKind::StreamEnd);
assert_eq!(AnyEvent::DocumentStart.kind(), EventKind::DocumentStart);
assert_eq!(AnyEvent::DocumentEnd.kind(), EventKind::DocumentEnd);
assert_eq!(AnyEvent::Alias.kind(), EventKind::Alias);
assert_eq!(AnyEvent::SequenceEnd.kind(), EventKind::SequenceEnd);
assert_eq!(AnyEvent::MappingEnd.kind(), EventKind::MappingEnd);
assert_eq!(
AnyEvent::Scalar(ScalarEvent::new(true, json!(0), None, None, None)).kind(),
EventKind::Scalar
);
assert_eq!(
AnyEvent::SequenceStart(SequenceStartEvent::new(true, None, None, None)).kind(),
EventKind::SequenceStart
);
assert_eq!(
AnyEvent::MappingStart(MappingStartEvent::new(true, None, None, None)).kind(),
EventKind::MappingStart
);
}
#[test]
fn composed_node_start_mark_returns_underlying_mark() {
let m = Some(Mark { line: 4, column: 9 });
let s = ScalarNode::new("t", json!(0), m, None, None);
let c = ComposedNode::Scalar(s);
assert_eq!(c.start_mark().unwrap().line, 4);
assert_eq!(c.start_mark().unwrap().column, 9);
}
}