blob: a0ce25f8ce40913d1fd7e944917b3c8e1e1a47a5 [file] [log] [blame]
use crate::libyaml;
use crate::libyaml::util::Owned;
use std::ffi::c_void;
use std::io;
use std::mem::{self, MaybeUninit};
use std::ptr::{self, addr_of_mut};
use std::slice;
use unsafe_libyaml as sys;
#[derive(Debug)]
pub(crate) enum Error {
Libyaml(libyaml::error::Error),
Io(io::Error),
}
pub(crate) struct Emitter<'a> {
pin: Owned<EmitterPinned<'a>>,
}
struct EmitterPinned<'a> {
sys: sys::yaml_emitter_t,
write: Box<dyn io::Write + 'a>,
write_error: Option<io::Error>,
}
#[derive(Debug)]
pub(crate) enum Event<'a> {
StreamStart,
StreamEnd,
DocumentStart,
DocumentEnd,
Scalar(Scalar<'a>),
SequenceStart(Sequence),
SequenceEnd,
MappingStart(Mapping),
MappingEnd,
}
#[derive(Debug)]
pub(crate) struct Scalar<'a> {
pub tag: Option<String>,
pub value: &'a str,
pub style: ScalarStyle,
}
#[derive(Debug)]
pub(crate) enum ScalarStyle {
Any,
Plain,
SingleQuoted,
Literal,
}
#[derive(Debug)]
pub(crate) struct Sequence {
pub tag: Option<String>,
}
#[derive(Debug)]
pub(crate) struct Mapping {
pub tag: Option<String>,
}
impl<'a> Emitter<'a> {
pub fn new(write: Box<dyn io::Write + 'a>) -> Emitter<'a> {
let owned = Owned::<EmitterPinned>::new_uninit();
let pin = unsafe {
let emitter = addr_of_mut!((*owned.ptr).sys);
if sys::yaml_emitter_initialize(emitter).fail {
panic!("malloc error: {}", libyaml::Error::emit_error(emitter));
}
sys::yaml_emitter_set_unicode(emitter, true);
sys::yaml_emitter_set_width(emitter, -1);
addr_of_mut!((*owned.ptr).write).write(write);
addr_of_mut!((*owned.ptr).write_error).write(None);
sys::yaml_emitter_set_output(emitter, write_handler, owned.ptr.cast());
Owned::assume_init(owned)
};
Emitter { pin }
}
pub fn emit(&mut self, event: Event) -> Result<(), Error> {
let mut sys_event = MaybeUninit::<sys::yaml_event_t>::uninit();
let sys_event = sys_event.as_mut_ptr();
unsafe {
let emitter = addr_of_mut!((*self.pin.ptr).sys);
let initialize_status = match event {
Event::StreamStart => {
sys::yaml_stream_start_event_initialize(sys_event, sys::YAML_UTF8_ENCODING)
}
Event::StreamEnd => sys::yaml_stream_end_event_initialize(sys_event),
Event::DocumentStart => {
let version_directive = ptr::null_mut();
let tag_directives_start = ptr::null_mut();
let tag_directives_end = ptr::null_mut();
let implicit = true;
sys::yaml_document_start_event_initialize(
sys_event,
version_directive,
tag_directives_start,
tag_directives_end,
implicit,
)
}
Event::DocumentEnd => {
let implicit = true;
sys::yaml_document_end_event_initialize(sys_event, implicit)
}
Event::Scalar(mut scalar) => {
let anchor = ptr::null();
let tag = scalar.tag.as_mut().map_or_else(ptr::null, |tag| {
tag.push('\0');
tag.as_ptr()
});
let value = scalar.value.as_ptr();
let length = scalar.value.len() as i32;
let plain_implicit = tag.is_null();
let quoted_implicit = tag.is_null();
let style = match scalar.style {
ScalarStyle::Any => sys::YAML_ANY_SCALAR_STYLE,
ScalarStyle::Plain => sys::YAML_PLAIN_SCALAR_STYLE,
ScalarStyle::SingleQuoted => sys::YAML_SINGLE_QUOTED_SCALAR_STYLE,
ScalarStyle::Literal => sys::YAML_LITERAL_SCALAR_STYLE,
};
sys::yaml_scalar_event_initialize(
sys_event,
anchor,
tag,
value,
length,
plain_implicit,
quoted_implicit,
style,
)
}
Event::SequenceStart(mut sequence) => {
let anchor = ptr::null();
let tag = sequence.tag.as_mut().map_or_else(ptr::null, |tag| {
tag.push('\0');
tag.as_ptr()
});
let implicit = tag.is_null();
let style = sys::YAML_ANY_SEQUENCE_STYLE;
sys::yaml_sequence_start_event_initialize(
sys_event, anchor, tag, implicit, style,
)
}
Event::SequenceEnd => sys::yaml_sequence_end_event_initialize(sys_event),
Event::MappingStart(mut mapping) => {
let anchor = ptr::null();
let tag = mapping.tag.as_mut().map_or_else(ptr::null, |tag| {
tag.push('\0');
tag.as_ptr()
});
let implicit = tag.is_null();
let style = sys::YAML_ANY_MAPPING_STYLE;
sys::yaml_mapping_start_event_initialize(
sys_event, anchor, tag, implicit, style,
)
}
Event::MappingEnd => sys::yaml_mapping_end_event_initialize(sys_event),
};
if initialize_status.fail {
return Err(Error::Libyaml(libyaml::Error::emit_error(emitter)));
}
if sys::yaml_emitter_emit(emitter, sys_event).fail {
return Err(self.error());
}
}
Ok(())
}
pub fn flush(&mut self) -> Result<(), Error> {
unsafe {
let emitter = addr_of_mut!((*self.pin.ptr).sys);
if sys::yaml_emitter_flush(emitter).fail {
return Err(self.error());
}
}
Ok(())
}
pub fn into_inner(self) -> Box<dyn io::Write + 'a> {
let sink = Box::new(io::sink());
unsafe { mem::replace(&mut (*self.pin.ptr).write, sink) }
}
fn error(&mut self) -> Error {
let emitter = unsafe { &mut *self.pin.ptr };
if let Some(write_error) = emitter.write_error.take() {
Error::Io(write_error)
} else {
Error::Libyaml(unsafe { libyaml::Error::emit_error(&emitter.sys) })
}
}
}
unsafe fn write_handler(data: *mut c_void, buffer: *mut u8, size: u64) -> i32 {
let data = data.cast::<EmitterPinned>();
match io::Write::write_all(
&mut *(*data).write,
slice::from_raw_parts(buffer, size as usize),
) {
Ok(()) => 1,
Err(err) => {
(*data).write_error = Some(err);
0
}
}
}
impl<'a> Drop for EmitterPinned<'a> {
fn drop(&mut self) {
unsafe { sys::yaml_emitter_delete(&mut self.sys) }
}
}