codeowners_validation/
parser.rs

1use globset::Glob;
2use std::fs::File;
3use std::io::{self, BufRead};
4
5#[derive(Eq, PartialEq, Clone)]
6pub struct CodeOwnerRule {
7    pub pattern: String,
8    pub owners: Vec<String>,
9    pub original_path: String,
10    pub glob: Glob,
11}
12
13pub struct InvalidLine {
14    pub line_number: usize,
15    pub content: String,
16}
17
18pub fn parse_codeowners_file(
19    file_path: &str,
20) -> io::Result<(Vec<CodeOwnerRule>, Vec<InvalidLine>)> {
21    let file = File::open(file_path)?;
22    let reader = io::BufReader::new(file);
23
24    let mut rules = Vec::new();
25    let mut invalid_lines = Vec::new();
26
27    for (line_number, line_result) in reader.lines().enumerate() {
28        if let Ok(line) = line_result {
29            let trimmed_line = line.trim();
30            if trimmed_line.is_empty() || trimmed_line.starts_with('#') {
31                // Skip empty lines and comments
32                continue;
33            }
34
35            let parts: Vec<&str> = line.split_whitespace().collect();
36            let pattern = parts[0].trim_matches('/').to_string();
37            let owners = parts[1..].iter().map(|s| s.to_string()).collect();
38            let original_path = parts[0].to_string();
39
40            let glob = match Glob::new(&format!("**{}", pattern)) {
41                Ok(glob) => glob,
42                Err(_) => {
43                    let invalid_line = InvalidLine {
44                        line_number: line_number + 1,
45                        content: line,
46                    };
47                    invalid_lines.push(invalid_line);
48                    continue;
49                }
50            };
51
52            let rule = CodeOwnerRule {
53                pattern,
54                owners,
55                original_path,
56                glob,
57            };
58
59            rules.push(rule);
60        }
61    }
62
63    Ok((rules, invalid_lines))
64}