#[derive(Debug, Clone)]
pub struct GeminiIgnoreIssue {
pub line: usize,
pub column: usize,
pub description: String,
}
pub fn validate_geminiignore(content: &str) -> Vec<GeminiIgnoreIssue> {
let mut issues = Vec::new();
let has_content = content.lines().any(|line| {
let trimmed = line.trim();
!trimmed.is_empty() && !trimmed.starts_with('#')
});
if !has_content {
issues.push(GeminiIgnoreIssue {
line: 1,
column: 0,
description: "empty".to_string(),
});
return issues;
}
for (line_num, line) in content.lines().enumerate() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let open_count = trimmed.chars().filter(|&c| c == '[').count();
let close_count = trimmed.chars().filter(|&c| c == ']').count();
if open_count != close_count {
issues.push(GeminiIgnoreIssue {
line: line_num + 1,
column: 0,
description: format!("syntax_error:{}", trimmed),
});
}
}
issues
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_geminiignore() {
let content = "node_modules/\n*.log\n.env\n";
let issues = validate_geminiignore(content);
assert!(issues.is_empty());
}
#[test]
fn test_valid_with_comments() {
let content = "# Ignore build artifacts\nbuild/\ndist/\n";
let issues = validate_geminiignore(content);
assert!(issues.is_empty());
}
#[test]
fn test_empty_content() {
let issues = validate_geminiignore("");
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].description, "empty");
}
#[test]
fn test_only_comments() {
let content = "# Comment 1\n# Comment 2\n";
let issues = validate_geminiignore(content);
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].description, "empty");
}
#[test]
fn test_only_whitespace() {
let content = " \n \n";
let issues = validate_geminiignore(content);
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].description, "empty");
}
#[test]
fn test_unmatched_bracket() {
let content = "*.log\n[unclosed\nnode_modules/\n";
let issues = validate_geminiignore(content);
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].line, 2);
assert!(issues[0].description.starts_with("syntax_error:"));
}
#[test]
fn test_valid_bracket_pattern() {
let content = "*.[oa]\n*.log\n";
let issues = validate_geminiignore(content);
assert!(issues.is_empty());
}
#[test]
fn test_multiple_issues() {
let content = "[bad\n*.log\n[also-bad\n";
let issues = validate_geminiignore(content);
assert_eq!(issues.len(), 2);
}
#[test]
fn test_negation_patterns() {
let content = "*.log\n!important.log\n";
let issues = validate_geminiignore(content);
assert!(issues.is_empty());
}
}