mirror of
https://github.com/simonask/libyaml-safer
synced 2024-11-22 19:33:03 +00:00
Move yaml_emitter_dump to Document
This commit is contained in:
parent
eae9fe8a4c
commit
1df2e6f998
4 changed files with 242 additions and 260 deletions
193
src/document.rs
193
src/document.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
255
src/dumper.rs
255
src/dumper.rs
|
@ -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)
|
||||
}
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
Loading…
Reference in a new issue