use crate::libyaml::cstr::{self, CStr}; use crate::libyaml::error::{Error, Mark, Result}; use crate::libyaml::tag::Tag; use crate::libyaml::util::Owned; use std::borrow::Cow; use std::fmt::{self, Debug}; use std::mem::MaybeUninit; use std::ptr::{addr_of_mut, NonNull}; use std::slice; use unsafe_libyaml as sys; pub(crate) struct Parser<'input> { pin: Owned>, } struct ParserPinned<'input> { sys: sys::yaml_parser_t, input: Cow<'input, [u8]>, } #[derive(Debug)] pub(crate) enum Event<'input> { StreamStart, StreamEnd, DocumentStart, DocumentEnd, Alias(Anchor), Scalar(Scalar<'input>), SequenceStart(SequenceStart), SequenceEnd, MappingStart(MappingStart), MappingEnd, } pub(crate) struct Scalar<'input> { pub anchor: Option, pub tag: Option, pub value: Box<[u8]>, pub style: ScalarStyle, pub repr: Option<&'input [u8]>, } #[derive(Debug)] pub(crate) struct SequenceStart { pub anchor: Option, pub tag: Option, } #[derive(Debug)] pub(crate) struct MappingStart { pub anchor: Option, pub tag: Option, } #[derive(Ord, PartialOrd, Eq, PartialEq)] pub(crate) struct Anchor(Box<[u8]>); #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub(crate) enum ScalarStyle { Plain, SingleQuoted, DoubleQuoted, Literal, Folded, } impl<'input> Parser<'input> { pub fn new(input: Cow<'input, [u8]>) -> Parser<'input> { let owned = Owned::::new_uninit(); let pin = unsafe { let parser = addr_of_mut!((*owned.ptr).sys); if sys::yaml_parser_initialize(parser).fail { panic!("malloc error: {}", Error::parse_error(parser)); } sys::yaml_parser_set_encoding(parser, sys::YAML_UTF8_ENCODING); sys::yaml_parser_set_input_string(parser, input.as_ptr(), input.len() as u64); addr_of_mut!((*owned.ptr).input).write(input); Owned::assume_init(owned) }; Parser { pin } } pub fn next(&mut self) -> Result<(Event<'input>, Mark)> { let mut event = MaybeUninit::::uninit(); unsafe { let parser = addr_of_mut!((*self.pin.ptr).sys); if (*parser).error != sys::YAML_NO_ERROR { return Err(Error::parse_error(parser)); } let event = event.as_mut_ptr(); if sys::yaml_parser_parse(parser, event).fail { return Err(Error::parse_error(parser)); } let ret = convert_event(&*event, &(*self.pin.ptr).input); let mark = Mark { sys: (*event).start_mark, }; sys::yaml_event_delete(event); Ok((ret, mark)) } } } unsafe fn convert_event<'input>( sys: &sys::yaml_event_t, input: &Cow<'input, [u8]>, ) -> Event<'input> { match sys.type_ { sys::YAML_STREAM_START_EVENT => Event::StreamStart, sys::YAML_STREAM_END_EVENT => Event::StreamEnd, sys::YAML_DOCUMENT_START_EVENT => Event::DocumentStart, sys::YAML_DOCUMENT_END_EVENT => Event::DocumentEnd, sys::YAML_ALIAS_EVENT => { Event::Alias(unsafe { optional_anchor(sys.data.alias.anchor) }.unwrap()) } sys::YAML_SCALAR_EVENT => Event::Scalar(Scalar { anchor: unsafe { optional_anchor(sys.data.scalar.anchor) }, tag: unsafe { optional_tag(sys.data.scalar.tag) }, value: Box::from(unsafe { slice::from_raw_parts(sys.data.scalar.value, sys.data.scalar.length as usize) }), style: match unsafe { sys.data.scalar.style } { sys::YAML_PLAIN_SCALAR_STYLE => ScalarStyle::Plain, sys::YAML_SINGLE_QUOTED_SCALAR_STYLE => ScalarStyle::SingleQuoted, sys::YAML_DOUBLE_QUOTED_SCALAR_STYLE => ScalarStyle::DoubleQuoted, sys::YAML_LITERAL_SCALAR_STYLE => ScalarStyle::Literal, sys::YAML_FOLDED_SCALAR_STYLE => ScalarStyle::Folded, sys::YAML_ANY_SCALAR_STYLE | _ => unreachable!(), }, repr: if let Cow::Borrowed(input) = input { Some(&input[sys.start_mark.index as usize..sys.end_mark.index as usize]) } else { None }, }), sys::YAML_SEQUENCE_START_EVENT => Event::SequenceStart(SequenceStart { anchor: unsafe { optional_anchor(sys.data.sequence_start.anchor) }, tag: unsafe { optional_tag(sys.data.sequence_start.tag) }, }), sys::YAML_SEQUENCE_END_EVENT => Event::SequenceEnd, sys::YAML_MAPPING_START_EVENT => Event::MappingStart(MappingStart { anchor: unsafe { optional_anchor(sys.data.mapping_start.anchor) }, tag: unsafe { optional_tag(sys.data.mapping_start.tag) }, }), sys::YAML_MAPPING_END_EVENT => Event::MappingEnd, sys::YAML_NO_EVENT => unreachable!(), _ => unimplemented!(), } } unsafe fn optional_anchor(anchor: *const u8) -> Option { let ptr = NonNull::new(anchor as *mut i8)?; let cstr = unsafe { CStr::from_ptr(ptr) }; Some(Anchor(Box::from(cstr.to_bytes()))) } unsafe fn optional_tag(tag: *const u8) -> Option { let ptr = NonNull::new(tag as *mut i8)?; let cstr = unsafe { CStr::from_ptr(ptr) }; Some(Tag(Box::from(cstr.to_bytes()))) } impl<'input> Debug for Scalar<'input> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let Scalar { anchor, tag, value, style, repr: _, } = self; struct LossySlice<'a>(&'a [u8]); impl<'a> Debug for LossySlice<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { cstr::debug_lossy(self.0, formatter) } } formatter .debug_struct("Scalar") .field("anchor", anchor) .field("tag", tag) .field("value", &LossySlice(value)) .field("style", style) .finish() } } impl Debug for Anchor { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { cstr::debug_lossy(&self.0, formatter) } } impl<'input> Drop for ParserPinned<'input> { fn drop(&mut self) { unsafe { sys::yaml_parser_delete(&mut self.sys) } } }