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>, } struct EmitterPinned<'a> { sys: sys::yaml_emitter_t, write: Box, write_error: Option, } #[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, 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, } #[derive(Debug)] pub(crate) struct Mapping { pub tag: Option, } impl<'a> Emitter<'a> { pub fn new(write: Box) -> Emitter<'a> { let owned = Owned::::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::::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 { 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::(); match io::Write::write_all(unsafe { &mut *(*data).write }, unsafe { slice::from_raw_parts(buffer, size as usize) }) { Ok(()) => 1, Err(err) => { unsafe { (*data).write_error = Some(err); } 0 } } } impl<'a> Drop for EmitterPinned<'a> { fn drop(&mut self) { unsafe { sys::yaml_emitter_delete(&mut self.sys) } } }