ruby_prism/parse_result/
diagnostics.rs

1//! Diagnostic handling for parse errors and warnings.
2
3use std::ffi::{c_char, CStr};
4use std::marker::PhantomData;
5use std::ptr::NonNull;
6
7use ruby_prism_sys::{pm_diagnostic_t, pm_parser_t};
8
9use super::Location;
10
11/// A diagnostic message that came back from the parser.
12#[derive(Debug)]
13pub struct Diagnostic<'pr> {
14    diag: NonNull<pm_diagnostic_t>,
15    parser: NonNull<pm_parser_t>,
16    marker: PhantomData<&'pr pm_diagnostic_t>,
17}
18
19impl<'pr> Diagnostic<'pr> {
20    /// Returns the message associated with the diagnostic.
21    ///
22    /// # Panics
23    ///
24    /// Panics if the message is not able to be converted into a `CStr`.
25    ///
26    #[must_use]
27    pub fn message(&self) -> &str {
28        unsafe {
29            let message: *mut c_char = self.diag.as_ref().message.cast_mut();
30            CStr::from_ptr(message).to_str().expect("prism allows only UTF-8 for diagnostics.")
31        }
32    }
33
34    /// The location of the diagnostic in the source.
35    #[must_use]
36    pub const fn location(&self) -> Location<'pr> {
37        Location::new(self.parser, unsafe { &self.diag.as_ref().location })
38    }
39}
40
41/// A struct created by the `errors` or `warnings` methods on `ParseResult`. It
42/// can be used to iterate over the diagnostics in the parse result.
43pub struct Diagnostics<'pr> {
44    diagnostic: *mut pm_diagnostic_t,
45    parser: NonNull<pm_parser_t>,
46    marker: PhantomData<&'pr pm_diagnostic_t>,
47}
48
49impl Diagnostics<'_> {
50    pub(crate) const fn new(diagnostic: *mut pm_diagnostic_t, parser: NonNull<pm_parser_t>) -> Self {
51        Diagnostics { diagnostic, parser, marker: PhantomData }
52    }
53}
54
55impl<'pr> Iterator for Diagnostics<'pr> {
56    type Item = Diagnostic<'pr>;
57
58    fn next(&mut self) -> Option<Self::Item> {
59        if let Some(diagnostic) = NonNull::new(self.diagnostic) {
60            let current = Diagnostic {
61                diag: diagnostic,
62                parser: self.parser,
63                marker: PhantomData,
64            };
65            self.diagnostic = unsafe { diagnostic.as_ref().node.next.cast::<pm_diagnostic_t>() };
66            Some(current)
67        } else {
68            None
69        }
70    }
71}