| 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<ParserPinned<'input>>, |
| } |
| |
| 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<Anchor>, |
| pub tag: Option<Tag>, |
| pub value: Box<[u8]>, |
| pub style: ScalarStyle, |
| pub repr: Option<&'input [u8]>, |
| } |
| |
| #[derive(Debug)] |
| pub(crate) struct SequenceStart { |
| pub anchor: Option<Anchor>, |
| pub tag: Option<Tag>, |
| } |
| |
| #[derive(Debug)] |
| pub(crate) struct MappingStart { |
| pub anchor: Option<Anchor>, |
| pub tag: Option<Tag>, |
| } |
| |
| #[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::<ParserPinned>::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::<sys::yaml_event_t>::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(optional_anchor(sys.data.alias.anchor).unwrap()), |
| sys::YAML_SCALAR_EVENT => Event::Scalar(Scalar { |
| anchor: optional_anchor(sys.data.scalar.anchor), |
| tag: optional_tag(sys.data.scalar.tag), |
| value: Box::from(slice::from_raw_parts( |
| sys.data.scalar.value, |
| sys.data.scalar.length as usize, |
| )), |
| style: match 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: optional_anchor(sys.data.sequence_start.anchor), |
| tag: optional_tag(sys.data.sequence_start.tag), |
| }), |
| sys::YAML_SEQUENCE_END_EVENT => Event::SequenceEnd, |
| sys::YAML_MAPPING_START_EVENT => Event::MappingStart(MappingStart { |
| anchor: optional_anchor(sys.data.mapping_start.anchor), |
| tag: 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<Anchor> { |
| let ptr = NonNull::new(anchor as *mut i8)?; |
| let cstr = CStr::from_ptr(ptr); |
| Some(Anchor(Box::from(cstr.to_bytes()))) |
| } |
| |
| unsafe fn optional_tag(tag: *const u8) -> Option<Tag> { |
| let ptr = NonNull::new(tag as *mut i8)?; |
| let cstr = 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) } |
| } |
| } |