Skip to main content

sentio_core/
syntax.rs

1use serde::Serialize;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5
6pub struct ParsedFile {
7    pub path: PathBuf,
8    pub source: String,
9    pub syntax: syn::File,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
13pub struct ParseFailure {
14    pub path: String,
15    pub message: String,
16}
17
18pub struct SyntaxReport {
19    pub files: Vec<ParsedFile>,
20    pub parse_failures: Vec<ParseFailure>,
21}
22
23pub fn parse_rust_files<I>(paths: I) -> SyntaxReport
24where
25    I: IntoIterator<Item = PathBuf>,
26{
27    let mut files = Vec::new();
28    let mut parse_failures = Vec::new();
29
30    for path in paths {
31        match parse_rust_file(&path) {
32            Ok(file) => files.push(file),
33            Err(failure) => parse_failures.push(failure),
34        }
35    }
36
37    SyntaxReport {
38        files,
39        parse_failures,
40    }
41}
42
43pub fn parse_rust_file(path: &Path) -> Result<ParsedFile, ParseFailure> {
44    let source = fs::read_to_string(path).map_err(|error| ParseFailure {
45        path: path.display().to_string(),
46        message: format!("failed to read file: {error}"),
47    })?;
48
49    parse_source(path, source)
50}
51
52fn parse_source(path: &Path, source: String) -> Result<ParsedFile, ParseFailure> {
53    let syntax = syn::parse_file(&source).map_err(|error| ParseFailure {
54        path: path.display().to_string(),
55        message: error.to_string(),
56    })?;
57
58    Ok(ParsedFile {
59        path: path.to_path_buf(),
60        source,
61        syntax,
62    })
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn parses_valid_rust_source() {
71        let path = Path::new("program/src/lib.rs");
72        let parsed = parse_source(path, "fn main() {}".to_string()).expect("source should parse");
73
74        assert_eq!(parsed.path, path);
75        assert_eq!(parsed.syntax.items.len(), 1);
76        assert_eq!(parsed.source, "fn main() {}");
77    }
78
79    #[test]
80    fn reports_invalid_rust_source() {
81        let path = Path::new("program/src/lib.rs");
82        let failure = match parse_source(path, "fn main( {".to_string()) {
83            Ok(_) => panic!("source should fail"),
84            Err(failure) => failure,
85        };
86
87        assert_eq!(failure.path, "program/src/lib.rs");
88        assert!(!failure.message.is_empty());
89    }
90}