ruby_prism/parse_result/
comments.rs

1//! Comment handling for the prism parser.
2
3use std::marker::PhantomData;
4use std::ptr::NonNull;
5
6use ruby_prism_sys::{pm_comment_t, pm_comment_type_t, pm_magic_comment_t, pm_parser_t};
7
8use super::Location;
9
10/// The type of the comment
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum CommentType {
13    /// `InlineComment` corresponds to comments that start with #.
14    InlineComment,
15    /// `EmbDocComment` corresponds to comments that are surrounded by =begin and =end.
16    EmbDocComment,
17}
18
19/// A comment that was found during parsing.
20#[derive(Debug)]
21pub struct Comment<'pr> {
22    content: NonNull<pm_comment_t>,
23    parser: NonNull<pm_parser_t>,
24    marker: PhantomData<&'pr pm_comment_t>,
25}
26
27impl<'pr> Comment<'pr> {
28    /// Returns the text of the comment.
29    ///
30    /// # Panics
31    /// Panics if the end offset is not greater than the start offset.
32    #[must_use]
33    pub fn text(&self) -> &[u8] {
34        self.location().as_slice()
35    }
36
37    /// Returns the type of the comment.
38    #[must_use]
39    pub fn type_(&self) -> CommentType {
40        let type_ = unsafe { self.content.as_ref().type_ };
41        if type_ == pm_comment_type_t::PM_COMMENT_EMBDOC {
42            CommentType::EmbDocComment
43        } else {
44            CommentType::InlineComment
45        }
46    }
47
48    /// The location of the comment in the source.
49    #[must_use]
50    pub const fn location(&self) -> Location<'pr> {
51        Location::new(self.parser, unsafe { &self.content.as_ref().location })
52    }
53}
54
55/// A struct created by the `comments` method on `ParseResult`. It can be used
56/// to iterate over the comments in the parse result.
57pub struct Comments<'pr> {
58    comment: *mut pm_comment_t,
59    parser: NonNull<pm_parser_t>,
60    marker: PhantomData<&'pr pm_comment_t>,
61}
62
63impl Comments<'_> {
64    pub(crate) const fn new(comment: *mut pm_comment_t, parser: NonNull<pm_parser_t>) -> Self {
65        Comments { comment, parser, marker: PhantomData }
66    }
67}
68
69impl<'pr> Iterator for Comments<'pr> {
70    type Item = Comment<'pr>;
71
72    fn next(&mut self) -> Option<Self::Item> {
73        if let Some(comment) = NonNull::new(self.comment) {
74            let current = Comment {
75                content: comment,
76                parser: self.parser,
77                marker: PhantomData,
78            };
79            self.comment = unsafe { comment.as_ref().node.next.cast::<pm_comment_t>() };
80            Some(current)
81        } else {
82            None
83        }
84    }
85}
86
87/// A magic comment that was found during parsing.
88#[derive(Debug)]
89pub struct MagicComment<'pr> {
90    parser: NonNull<pm_parser_t>,
91    comment: NonNull<pm_magic_comment_t>,
92    marker: PhantomData<&'pr pm_magic_comment_t>,
93}
94
95impl MagicComment<'_> {
96    /// Returns the text of the comment's key.
97    #[must_use]
98    pub const fn key(&self) -> &[u8] {
99        unsafe {
100            let start = self.parser.as_ref().start.add(self.comment.as_ref().key.start as usize);
101            let len = self.comment.as_ref().key.length as usize;
102            std::slice::from_raw_parts(start, len)
103        }
104    }
105
106    /// Returns the text of the comment's value.
107    #[must_use]
108    pub const fn value(&self) -> &[u8] {
109        unsafe {
110            let start = self.parser.as_ref().start.add(self.comment.as_ref().value.start as usize);
111            let len = self.comment.as_ref().value.length as usize;
112            std::slice::from_raw_parts(start, len)
113        }
114    }
115}
116
117/// A struct created by the `magic_comments` method on `ParseResult`. It can be used
118/// to iterate over the magic comments in the parse result.
119pub struct MagicComments<'pr> {
120    parser: NonNull<pm_parser_t>,
121    comment: *mut pm_magic_comment_t,
122    marker: PhantomData<&'pr pm_magic_comment_t>,
123}
124
125impl MagicComments<'_> {
126    pub(crate) const fn new(parser: NonNull<pm_parser_t>, comment: *mut pm_magic_comment_t) -> Self {
127        MagicComments { parser, comment, marker: PhantomData }
128    }
129}
130
131impl<'pr> Iterator for MagicComments<'pr> {
132    type Item = MagicComment<'pr>;
133
134    fn next(&mut self) -> Option<Self::Item> {
135        if let Some(comment) = NonNull::new(self.comment) {
136            let current = MagicComment { parser: self.parser, comment, marker: PhantomData };
137            self.comment = unsafe { comment.as_ref().node.next.cast::<pm_magic_comment_t>() };
138            Some(current)
139        } else {
140            None
141        }
142    }
143}