codeowners_validation/
parser.rs1use 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 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}