Skip to main content

sentio_core/
syntax.rs

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