use crate::libyaml::cstr::CStr; use std::fmt::{self, Debug, Display}; use std::mem::MaybeUninit; use std::ptr::NonNull; use unsafe_libyaml as sys; pub(crate) type Result = std::result::Result; pub(crate) struct Error { kind: sys::yaml_error_type_t, problem: CStr<'static>, problem_offset: u64, problem_mark: Mark, context: Option>, context_mark: Mark, } impl Error { pub unsafe fn parse_error(parser: *const sys::yaml_parser_t) -> Self { Error { kind: unsafe { (*parser).error }, problem: match NonNull::new(unsafe { (*parser).problem as *mut _ }) { Some(problem) => unsafe { CStr::from_ptr(problem) }, None => CStr::from_bytes_with_nul(b"libyaml parser failed but there is no error\0"), }, problem_offset: unsafe { (*parser).problem_offset }, problem_mark: Mark { sys: unsafe { (*parser).problem_mark }, }, context: match NonNull::new(unsafe { (*parser).context as *mut _ }) { Some(context) => Some(unsafe { CStr::from_ptr(context) }), None => None, }, context_mark: Mark { sys: unsafe { (*parser).context_mark }, }, } } pub unsafe fn emit_error(emitter: *const sys::yaml_emitter_t) -> Self { Error { kind: unsafe { (*emitter).error }, problem: match NonNull::new(unsafe { (*emitter).problem as *mut _ }) { Some(problem) => unsafe { CStr::from_ptr(problem) }, None => { CStr::from_bytes_with_nul(b"libyaml emitter failed but there is no error\0") } }, problem_offset: 0, problem_mark: Mark { sys: unsafe { MaybeUninit::::zeroed().assume_init() }, }, context: None, context_mark: Mark { sys: unsafe { MaybeUninit::::zeroed().assume_init() }, }, } } pub fn mark(&self) -> Mark { self.problem_mark } } impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "{}", self.problem)?; if self.problem_mark.sys.line != 0 || self.problem_mark.sys.column != 0 { write!(formatter, " at {}", self.problem_mark)?; } else if self.problem_offset != 0 { write!(formatter, " at position {}", self.problem_offset)?; } if let Some(context) = &self.context { write!(formatter, ", {}", context)?; if (self.context_mark.sys.line != 0 || self.context_mark.sys.column != 0) && (self.context_mark.sys.line != self.problem_mark.sys.line || self.context_mark.sys.column != self.problem_mark.sys.column) { write!(formatter, " at {}", self.context_mark)?; } } Ok(()) } } impl Debug for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut formatter = formatter.debug_struct("Error"); if let Some(kind) = match self.kind { sys::YAML_MEMORY_ERROR => Some("MEMORY"), sys::YAML_READER_ERROR => Some("READER"), sys::YAML_SCANNER_ERROR => Some("SCANNER"), sys::YAML_PARSER_ERROR => Some("PARSER"), sys::YAML_COMPOSER_ERROR => Some("COMPOSER"), sys::YAML_WRITER_ERROR => Some("WRITER"), sys::YAML_EMITTER_ERROR => Some("EMITTER"), _ => None, } { formatter.field("kind", &format_args!("{}", kind)); } formatter.field("problem", &self.problem); if self.problem_mark.sys.line != 0 || self.problem_mark.sys.column != 0 { formatter.field("problem_mark", &self.problem_mark); } else if self.problem_offset != 0 { formatter.field("problem_offset", &self.problem_offset); } if let Some(context) = &self.context { formatter.field("context", context); if self.context_mark.sys.line != 0 || self.context_mark.sys.column != 0 { formatter.field("context_mark", &self.context_mark); } } formatter.finish() } } #[derive(Copy, Clone)] pub(crate) struct Mark { pub(super) sys: sys::yaml_mark_t, } impl Mark { pub fn index(&self) -> u64 { self.sys.index } pub fn line(&self) -> u64 { self.sys.line } pub fn column(&self) -> u64 { self.sys.column } } impl Display for Mark { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { if self.sys.line != 0 || self.sys.column != 0 { write!( formatter, "line {} column {}", self.sys.line + 1, self.sys.column + 1, ) } else { write!(formatter, "position {}", self.sys.index) } } } impl Debug for Mark { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut formatter = formatter.debug_struct("Mark"); if self.sys.line != 0 || self.sys.column != 0 { formatter.field("line", &(self.sys.line + 1)); formatter.field("column", &(self.sys.column + 1)); } else { formatter.field("index", &self.sys.index); } formatter.finish() } }