litcheck_filecheck/ast/
file.rs

1use crate::{check::CheckProgram, common::*};
2
3use super::{CheckPattern, CheckType};
4
5/// A check file is the source file we're going to check matches some set of rules.
6///
7/// The rules to check are found by parsing the file into lines, and recognizing
8/// certain special patterns that indicate what checks are expected to be performed.
9///
10/// Once checks are parsed from a file, the file is checked line-by-line, using the
11/// parsed checks to determine if the file passes or not.
12#[derive(Debug)]
13pub struct CheckFile<'a> {
14    /// There should be an entry in this vector for every line in the source file
15    lines: Vec<CheckLine<'a>>,
16}
17impl<'a> CheckFile<'a> {
18    pub fn new(lines: Vec<CheckLine<'a>>) -> Self {
19        Self { lines }
20    }
21
22    pub fn lines(&self) -> &[CheckLine<'a>] {
23        self.lines.as_slice()
24    }
25
26    pub fn into_lines(self) -> Vec<CheckLine<'a>> {
27        self.lines
28    }
29
30    pub fn compile(
31        self,
32        config: &Config,
33        interner: &mut StringInterner,
34    ) -> DiagResult<CheckProgram<'a>> {
35        CheckProgram::compile(self, config, interner)
36    }
37}
38
39/// A check line represents a line in a check file, and is associated with some
40/// check type and pattern.
41#[derive(Debug)]
42pub struct CheckLine<'a> {
43    /// Where in the source file that the check was specified
44    pub span: SourceSpan,
45    /// Any comment lines preceding this check in the sourrce file
46    pub comment: Vec<Cow<'a, str>>,
47    /// The type of check represented
48    pub ty: CheckType,
49    /// The pattern to match
50    pub pattern: CheckPattern<'a>,
51}
52impl<'a> CheckLine<'a> {
53    pub fn new(span: SourceSpan, ty: CheckType, pattern: CheckPattern<'a>) -> Self {
54        Self {
55            span,
56            comment: vec![],
57            ty,
58            pattern,
59        }
60    }
61
62    #[inline(always)]
63    pub fn kind(&self) -> Check {
64        self.ty.kind
65    }
66
67    pub fn is_regex_compatible(&self) -> bool {
68        self.pattern.is_regex_compatible()
69    }
70
71    pub fn with_comment(mut self, comment: Cow<'a, str>) -> Self {
72        if comment.is_empty() {
73            return self;
74        }
75        self.comment.push(comment);
76        self
77    }
78
79    pub fn into_comment(mut self) -> Cow<'a, str> {
80        match self.comment.len() {
81            0 => Cow::Borrowed(""),
82            1 => self.comment.pop().unwrap(),
83            n => {
84                let len = self.comment.iter().map(|c| c.len()).sum::<usize>() + n;
85                Cow::Owned(self.comment.into_iter().fold(
86                    String::with_capacity(len),
87                    |mut buf, c| {
88                        if !buf.is_empty() {
89                            buf.push('\n');
90                        }
91                        buf.push_str(&c);
92                        buf
93                    },
94                ))
95            }
96        }
97    }
98
99    pub fn prepend_comment(&mut self, comment: Cow<'a, str>) {
100        if comment.is_empty() {
101            return;
102        }
103        self.comment.insert(0, comment);
104    }
105}
106impl<'a> Spanned for CheckLine<'a> {
107    fn span(&self) -> SourceSpan {
108        self.span
109    }
110}
111impl<'a> Eq for CheckLine<'a> {}
112impl<'a> PartialEq for CheckLine<'a> {
113    fn eq(&self, other: &Self) -> bool {
114        self.ty == other.ty && self.pattern == other.pattern && self.comment == other.comment
115    }
116}