ruby_prism/parse_result/
mod.rs1mod comments;
7mod diagnostics;
8
9use std::ptr::NonNull;
10
11use ruby_prism_sys::{pm_comment_t, pm_diagnostic_t, pm_location_t, pm_magic_comment_t, pm_node_destroy, pm_node_t, pm_parser_free, pm_parser_t};
12
13pub use self::comments::{Comment, CommentType, Comments, MagicComment, MagicComments};
14pub use self::diagnostics::{Diagnostic, Diagnostics};
15
16use crate::Node;
17
18pub struct Location<'pr> {
20 pub(crate) parser: NonNull<pm_parser_t>,
21 pub(crate) start: u32,
22 pub(crate) length: u32,
23 marker: std::marker::PhantomData<&'pr [u8]>,
24}
25
26impl<'pr> Location<'pr> {
27 #[must_use]
29 pub fn as_slice(&self) -> &'pr [u8] {
30 unsafe {
31 let parser_start = (*self.parser.as_ptr()).start;
32 std::slice::from_raw_parts(parser_start.add(self.start as usize), self.length as usize)
33 }
34 }
35
36 #[must_use]
38 pub(crate) const fn new(parser: NonNull<pm_parser_t>, location: &'pr pm_location_t) -> Self {
39 Location {
40 parser,
41 start: location.start,
42 length: location.length,
43 marker: std::marker::PhantomData,
44 }
45 }
46
47 #[must_use]
49 pub const fn end(&self) -> u32 {
50 self.start + self.length
51 }
52
53 #[must_use]
57 pub fn join(&self, other: &Self) -> Option<Self> {
58 if self.parser != other.parser || self.start > other.start {
59 None
60 } else {
61 Some(Location {
62 parser: self.parser,
63 start: self.start,
64 length: other.end() - self.start,
65 marker: std::marker::PhantomData,
66 })
67 }
68 }
69}
70
71impl std::fmt::Debug for Location<'_> {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 let slice: &[u8] = self.as_slice();
74
75 let mut visible = String::new();
76 visible.push('"');
77
78 for &byte in slice {
79 let part: Vec<u8> = std::ascii::escape_default(byte).collect();
80 visible.push_str(std::str::from_utf8(&part).unwrap());
81 }
82
83 visible.push('"');
84 write!(f, "{visible}")
85 }
86}
87
88#[derive(Debug)]
90pub struct ParseResult<'pr> {
91 source: &'pr [u8],
92 parser: NonNull<pm_parser_t>,
93 node: NonNull<pm_node_t>,
94}
95
96impl<'pr> ParseResult<'pr> {
97 pub(crate) const unsafe fn new(source: &'pr [u8], parser: NonNull<pm_parser_t>, node: NonNull<pm_node_t>) -> Self {
98 ParseResult { source, parser, node }
99 }
100
101 #[must_use]
103 pub const fn source(&self) -> &'pr [u8] {
104 self.source
105 }
106
107 #[must_use]
109 pub fn frozen_string_literals(&self) -> bool {
110 unsafe { (*self.parser.as_ptr()).frozen_string_literal == 1 }
111 }
112
113 #[must_use]
116 pub fn as_slice(&self, location: &Location<'pr>) -> &'pr [u8] {
117 let start = location.start as usize;
118 let end = start + location.length as usize;
119 &self.source[start..end]
120 }
121
122 #[must_use]
125 pub fn line_offsets(&self) -> &'pr [u32] {
126 unsafe {
127 let list = &(*self.parser.as_ptr()).line_offsets;
128 std::slice::from_raw_parts(list.offsets, list.size)
129 }
130 }
131 #[must_use]
134 pub fn errors(&self) -> Diagnostics<'_> {
135 unsafe {
136 let list = &mut (*self.parser.as_ptr()).error_list;
137 Diagnostics::new(list.head.cast::<pm_diagnostic_t>(), self.parser)
138 }
139 }
140
141 #[must_use]
144 pub fn warnings(&self) -> Diagnostics<'_> {
145 unsafe {
146 let list = &mut (*self.parser.as_ptr()).warning_list;
147 Diagnostics::new(list.head.cast::<pm_diagnostic_t>(), self.parser)
148 }
149 }
150
151 #[must_use]
154 pub fn comments(&self) -> Comments<'_> {
155 unsafe {
156 let list = &mut (*self.parser.as_ptr()).comment_list;
157 Comments::new(list.head.cast::<pm_comment_t>(), self.parser)
158 }
159 }
160
161 #[must_use]
164 pub fn magic_comments(&self) -> MagicComments<'_> {
165 unsafe {
166 let list = &mut (*self.parser.as_ptr()).magic_comment_list;
167 MagicComments::new(self.parser, list.head.cast::<pm_magic_comment_t>())
168 }
169 }
170
171 #[must_use]
173 pub fn data_loc(&self) -> Option<Location<'_>> {
174 let location = unsafe { &(*self.parser.as_ptr()).data_loc };
175 if location.length == 0 {
176 None
177 } else {
178 Some(Location::new(self.parser, location))
179 }
180 }
181
182 #[must_use]
184 pub fn node(&self) -> Node<'_> {
185 Node::new(self.parser, self.node.as_ptr())
186 }
187
188 #[must_use]
191 pub fn is_success(&self) -> bool {
192 self.errors().next().is_none()
193 }
194
195 #[must_use]
198 pub fn is_failure(&self) -> bool {
199 !self.is_success()
200 }
201}
202
203impl Drop for ParseResult<'_> {
204 fn drop(&mut self) {
205 unsafe {
206 pm_node_destroy(self.parser.as_ptr(), self.node.as_ptr());
207 pm_parser_free(self.parser.as_ptr());
208 drop(Box::from_raw(self.parser.as_ptr()));
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use crate::parse;
216
217 #[test]
218 fn test_is_success() {
219 let result = parse(b"1 + 1");
220 assert!(result.is_success());
221 assert!(!result.is_failure());
222 }
223
224 #[test]
225 fn test_is_failure() {
226 let result = parse(b"<>");
227 assert!(result.is_failure());
228 assert!(!result.is_success());
229 }
230}