Move yaml_emitter_dump to Document

This commit is contained in:
Simon Ask Ulsnes 2024-02-04 11:04:25 +01:00
parent eae9fe8a4c
commit 1df2e6f998
4 changed files with 242 additions and 260 deletions

View file

@ -1,7 +1,7 @@
use crate::{
AliasData, ComposerError, Event, EventData, MappingStyle, Mark, Parser, ScalarStyle,
SequenceStyle, TagDirective, VersionDirective, DEFAULT_MAPPING_TAG, DEFAULT_SCALAR_TAG,
DEFAULT_SEQUENCE_TAG,
AliasData, Anchors, ComposerError, Emitter, EmitterError, Event, EventData, MappingStyle, Mark,
Parser, ScalarStyle, SequenceStyle, TagDirective, VersionDirective, DEFAULT_MAPPING_TAG,
DEFAULT_SCALAR_TAG, DEFAULT_SEQUENCE_TAG,
};
/// The document structure.
@ -606,4 +606,191 @@ impl Document {
_ = ctx.pop();
Ok(())
}
/// Emit a YAML document.
///
/// The document object may be generated using the
/// [`yaml_parser_load()`](crate::yaml_parser_load) function or the
/// [`Document::new()`] function.
pub fn dump(mut self, emitter: &mut Emitter) -> Result<(), EmitterError> {
if !emitter.opened {
if let Err(err) = emitter.open() {
emitter.reset_anchors();
return Err(err);
}
}
if self.nodes.is_empty() {
// TODO: Do we really want to close the emitter just because the
// document contains no nodes? Isn't it OK to emit multiple documents in
// the same stream?
emitter.close()?;
} else {
assert!(emitter.opened);
emitter.anchors = vec![Anchors::default(); self.nodes.len()];
let event = Event {
data: EventData::DocumentStart {
version_directive: self.version_directive,
tag_directives: core::mem::take(&mut self.tag_directives),
implicit: self.start_implicit,
},
..Default::default()
};
emitter.emit(event)?;
self.anchor_node(emitter, 1);
self.dump_node(emitter, 1)?;
let event = Event {
data: EventData::DocumentEnd {
implicit: self.end_implicit,
},
..Default::default()
};
emitter.emit(event)?;
}
emitter.reset_anchors();
Ok(())
}
fn anchor_node(&self, emitter: &mut Emitter, index: i32) {
let node = &self.nodes[index as usize - 1];
emitter.anchors[index as usize - 1].references += 1;
if emitter.anchors[index as usize - 1].references == 1 {
match &node.data {
NodeData::Sequence { items, .. } => {
for item in items {
emitter.anchor_node_sub(*item);
}
}
NodeData::Mapping { pairs, .. } => {
for pair in pairs {
emitter.anchor_node_sub(pair.key);
emitter.anchor_node_sub(pair.value);
}
}
_ => {}
}
} else if emitter.anchors[index as usize - 1].references == 2 {
emitter.last_anchor_id += 1;
emitter.anchors[index as usize - 1].anchor = emitter.last_anchor_id;
}
}
fn dump_node(&mut self, emitter: &mut Emitter, index: i32) -> Result<(), EmitterError> {
let node = &mut self.nodes[index as usize - 1];
let anchor_id: i32 = emitter.anchors[index as usize - 1].anchor;
let mut anchor: Option<String> = None;
if anchor_id != 0 {
anchor = Some(Emitter::generate_anchor(anchor_id));
}
if emitter.anchors[index as usize - 1].serialized {
return Self::dump_alias(emitter, anchor.unwrap());
}
emitter.anchors[index as usize - 1].serialized = true;
let node = core::mem::take(node);
match node.data {
NodeData::Scalar { .. } => Self::dump_scalar(emitter, node, anchor),
NodeData::Sequence { .. } => self.dump_sequence(emitter, node, anchor),
NodeData::Mapping { .. } => self.dump_mapping(emitter, node, anchor),
_ => unreachable!("document node is neither a scalar, sequence, or a mapping"),
}
}
fn dump_alias(emitter: &mut Emitter, anchor: String) -> Result<(), EmitterError> {
let event = Event {
data: EventData::Alias { anchor },
..Default::default()
};
emitter.emit(event)
}
fn dump_scalar(
emitter: &mut Emitter,
node: Node,
anchor: Option<String>,
) -> Result<(), EmitterError> {
let plain_implicit = node.tag.as_deref() == Some(DEFAULT_SCALAR_TAG);
let quoted_implicit = node.tag.as_deref() == Some(DEFAULT_SCALAR_TAG); // TODO: Why compare twice?! (even the C code does this)
let NodeData::Scalar { value, style } = node.data else {
unreachable!()
};
let event = Event {
data: EventData::Scalar {
anchor,
tag: node.tag,
value,
plain_implicit,
quoted_implicit,
style,
},
..Default::default()
};
emitter.emit(event)
}
fn dump_sequence(
&mut self,
emitter: &mut Emitter,
node: Node,
anchor: Option<String>,
) -> Result<(), EmitterError> {
let implicit = node.tag.as_deref() == Some(DEFAULT_SEQUENCE_TAG);
let NodeData::Sequence { items, style } = node.data else {
unreachable!()
};
let event = Event {
data: EventData::SequenceStart {
anchor,
tag: node.tag,
implicit,
style,
},
..Default::default()
};
emitter.emit(event)?;
for item in items {
self.dump_node(emitter, item)?;
}
let event = Event {
data: EventData::SequenceEnd,
..Default::default()
};
emitter.emit(event)
}
fn dump_mapping(
&mut self,
emitter: &mut Emitter,
node: Node,
anchor: Option<String>,
) -> Result<(), EmitterError> {
let implicit = node.tag.as_deref() == Some(DEFAULT_MAPPING_TAG);
let NodeData::Mapping { pairs, style } = node.data else {
unreachable!()
};
let event = Event {
data: EventData::MappingStart {
anchor,
tag: node.tag,
implicit,
style,
},
..Default::default()
};
emitter.emit(event)?;
for pair in pairs {
self.dump_node(emitter, pair.key)?;
self.dump_node(emitter, pair.value)?;
}
let event = Event {
data: EventData::MappingEnd,
..Default::default()
};
emitter.emit(event)
}
}

View file

@ -1,255 +0,0 @@
use std::mem::take;
use alloc::string::String;
use alloc::vec;
use crate::{Anchors, Document, Emitter, Event, EventData, Node, NodeData};
use crate::{
EmitterError, Encoding, DEFAULT_MAPPING_TAG, DEFAULT_SCALAR_TAG, DEFAULT_SEQUENCE_TAG,
};
/// Start a YAML stream.
///
/// This function should be used before
/// [`yaml_emitter_dump()`](crate::yaml_emitter_dump) is called.
pub fn yaml_emitter_open(emitter: &mut Emitter) -> Result<(), EmitterError> {
assert!(!emitter.opened);
let event = Event {
data: EventData::StreamStart {
encoding: Encoding::Any,
},
..Default::default()
};
emitter.emit(event)?;
emitter.opened = true;
Ok(())
}
/// Finish a YAML stream.
///
/// This function should be used after
/// [`yaml_emitter_dump()`](crate::yaml_emitter_dump) is called.
pub fn yaml_emitter_close(emitter: &mut Emitter) -> Result<(), EmitterError> {
assert!(emitter.opened);
if emitter.closed {
return Ok(());
}
let event = Event {
data: EventData::StreamEnd,
..Default::default()
};
emitter.emit(event)?;
emitter.closed = true;
Ok(())
}
/// Emit a YAML document.
///
/// The document object may be generated using the
/// [`yaml_parser_load()`](crate::yaml_parser_load) function or the
/// [`Document::new()`] function.
pub fn yaml_emitter_dump(
emitter: &mut Emitter,
mut document: Document,
) -> Result<(), EmitterError> {
if !emitter.opened {
if let Err(err) = yaml_emitter_open(emitter) {
yaml_emitter_reset_anchors(emitter);
return Err(err);
}
}
if document.nodes.is_empty() {
// TODO: Do we really want to close the emitter just because the
// document contains no nodes? Isn't it OK to emit multiple documents in
// the same stream?
yaml_emitter_close(emitter)?;
} else {
assert!(emitter.opened);
emitter.anchors = vec![Anchors::default(); document.nodes.len()];
let event = Event {
data: EventData::DocumentStart {
version_directive: document.version_directive,
tag_directives: take(&mut document.tag_directives),
implicit: document.start_implicit,
},
..Default::default()
};
emitter.emit(event)?;
yaml_emitter_anchor_node(emitter, &document, 1);
yaml_emitter_dump_node(emitter, &mut document, 1)?;
let event = Event {
data: EventData::DocumentEnd {
implicit: document.end_implicit,
},
..Default::default()
};
emitter.emit(event)?;
}
yaml_emitter_reset_anchors(emitter);
Ok(())
}
fn yaml_emitter_reset_anchors(emitter: &mut Emitter) {
emitter.anchors.clear();
emitter.last_anchor_id = 0;
}
fn yaml_emitter_anchor_node_sub(emitter: &mut Emitter, index: i32) {
emitter.anchors[index as usize - 1].references += 1;
if emitter.anchors[index as usize - 1].references == 2 {
emitter.last_anchor_id += 1;
emitter.anchors[index as usize - 1].anchor = emitter.last_anchor_id;
}
}
fn yaml_emitter_anchor_node(emitter: &mut Emitter, document: &Document, index: i32) {
let node = &document.nodes[index as usize - 1];
emitter.anchors[index as usize - 1].references += 1;
if emitter.anchors[index as usize - 1].references == 1 {
match &node.data {
NodeData::Sequence { items, .. } => {
for item in items {
yaml_emitter_anchor_node_sub(emitter, *item);
}
}
NodeData::Mapping { pairs, .. } => {
for pair in pairs {
yaml_emitter_anchor_node_sub(emitter, pair.key);
yaml_emitter_anchor_node_sub(emitter, pair.value);
}
}
_ => {}
}
} else if emitter.anchors[index as usize - 1].references == 2 {
emitter.last_anchor_id += 1;
emitter.anchors[index as usize - 1].anchor = emitter.last_anchor_id;
}
}
fn yaml_emitter_generate_anchor(_emitter: &mut Emitter, anchor_id: i32) -> String {
alloc::format!("id{anchor_id:03}")
}
fn yaml_emitter_dump_node(
emitter: &mut Emitter,
document: &mut Document,
index: i32,
) -> Result<(), EmitterError> {
let node = &mut document.nodes[index as usize - 1];
let anchor_id: i32 = emitter.anchors[index as usize - 1].anchor;
let mut anchor: Option<String> = None;
if anchor_id != 0 {
anchor = Some(yaml_emitter_generate_anchor(emitter, anchor_id));
}
if emitter.anchors[index as usize - 1].serialized {
return yaml_emitter_dump_alias(emitter, anchor.unwrap());
}
emitter.anchors[index as usize - 1].serialized = true;
let node = take(node);
match node.data {
NodeData::Scalar { .. } => yaml_emitter_dump_scalar(emitter, node, anchor),
NodeData::Sequence { .. } => yaml_emitter_dump_sequence(emitter, document, node, anchor),
NodeData::Mapping { .. } => yaml_emitter_dump_mapping(emitter, document, node, anchor),
_ => unreachable!("document node is neither a scalar, sequence, or a mapping"),
}
}
fn yaml_emitter_dump_alias(emitter: &mut Emitter, anchor: String) -> Result<(), EmitterError> {
let event = Event {
data: EventData::Alias { anchor },
..Default::default()
};
emitter.emit(event)
}
fn yaml_emitter_dump_scalar(
emitter: &mut Emitter,
node: Node,
anchor: Option<String>,
) -> Result<(), EmitterError> {
let plain_implicit = node.tag.as_deref() == Some(DEFAULT_SCALAR_TAG);
let quoted_implicit = node.tag.as_deref() == Some(DEFAULT_SCALAR_TAG); // TODO: Why compare twice?! (even the C code does this)
let NodeData::Scalar { value, style } = node.data else {
unreachable!()
};
let event = Event {
data: EventData::Scalar {
anchor,
tag: node.tag,
value,
plain_implicit,
quoted_implicit,
style,
},
..Default::default()
};
emitter.emit(event)
}
fn yaml_emitter_dump_sequence(
emitter: &mut Emitter,
document: &mut Document,
node: Node,
anchor: Option<String>,
) -> Result<(), EmitterError> {
let implicit = node.tag.as_deref() == Some(DEFAULT_SEQUENCE_TAG);
let NodeData::Sequence { items, style } = node.data else {
unreachable!()
};
let event = Event {
data: EventData::SequenceStart {
anchor,
tag: node.tag,
implicit,
style,
},
..Default::default()
};
emitter.emit(event)?;
for item in items {
yaml_emitter_dump_node(emitter, document, item)?;
}
let event = Event {
data: EventData::SequenceEnd,
..Default::default()
};
emitter.emit(event)
}
fn yaml_emitter_dump_mapping(
emitter: &mut Emitter,
document: &mut Document,
node: Node,
anchor: Option<String>,
) -> Result<(), EmitterError> {
let implicit = node.tag.as_deref() == Some(DEFAULT_MAPPING_TAG);
let NodeData::Mapping { pairs, style } = node.data else {
unreachable!()
};
let event = Event {
data: EventData::MappingStart {
anchor,
tag: node.tag,
implicit,
style,
},
..Default::default()
};
emitter.emit(event)?;
for pair in pairs {
yaml_emitter_dump_node(emitter, document, pair.key)?;
yaml_emitter_dump_node(emitter, document, pair.value)?;
}
let event = Event {
data: EventData::MappingEnd,
..Default::default()
};
emitter.emit(event)
}

View file

@ -278,6 +278,41 @@ impl<'w> Emitter<'w> {
*self = Self::new();
}
/// Start a YAML stream.
///
/// This function should be used before
/// [`yaml_emitter_dump()`](crate::yaml_emitter_dump) is called.
pub fn open(&mut self) -> Result<(), EmitterError> {
assert!(!self.opened);
let event = Event {
data: EventData::StreamStart {
encoding: Encoding::Any,
},
..Default::default()
};
self.emit(event)?;
self.opened = true;
Ok(())
}
/// Finish a YAML stream.
///
/// This function should be used after
/// [`yaml_emitter_dump()`](crate::yaml_emitter_dump) is called.
pub fn close(&mut self) -> Result<(), EmitterError> {
assert!(self.opened);
if self.closed {
return Ok(());
}
let event = Event {
data: EventData::StreamEnd,
..Default::default()
};
self.emit(event)?;
self.closed = true;
Ok(())
}
/// Set a string output.
///
/// The emitter will write the output characters to the `output` buffer.
@ -1758,4 +1793,21 @@ impl<'w> Emitter<'w> {
}
Ok(())
}
pub(crate) fn reset_anchors(&mut self) {
self.anchors.clear();
self.last_anchor_id = 0;
}
pub(crate) fn anchor_node_sub(&mut self, index: i32) {
self.anchors[index as usize - 1].references += 1;
if self.anchors[index as usize - 1].references == 2 {
self.last_anchor_id += 1;
self.anchors[index as usize - 1].anchor = self.last_anchor_id;
}
}
pub(crate) fn generate_anchor(anchor_id: i32) -> String {
alloc::format!("id{anchor_id:03}")
}
}

View file

@ -28,7 +28,6 @@ extern crate alloc;
mod macros;
mod document;
mod dumper;
mod emitter;
mod error;
mod event;
@ -39,7 +38,6 @@ mod token;
mod writer;
pub use crate::document::*;
pub use crate::dumper::{yaml_emitter_close, yaml_emitter_dump, yaml_emitter_open};
pub use crate::emitter::*;
pub use crate::error::*;
pub use crate::event::*;