git_ignore_tool/
lib.rs

1//! Git-ignore library for managing git ignore files
2//!
3//! This library provides functionality to add patterns to various git ignore files:
4//! - Repository `.gitignore`
5//! - Local `.git/info/exclude`
6//! - Global gitignore file
7//!
8//! # Examples
9//!
10//! ```no_run
11//! use git_ignore_tool::{add_patterns_to_gitignore, PatternValidationLevel};
12//!
13//! let patterns = vec!["*.log".to_string(), "build/".to_string()];
14//! add_patterns_to_gitignore(&patterns, PatternValidationLevel::Warn)?;
15//! # Ok::<(), Box<dyn std::error::Error>>(())
16//! ```
17
18pub mod git;
19pub mod ignore;
20
21use anyhow::bail;
22
23/// Validate patterns for library usage (simpler than CLI validation)
24fn validate_patterns_for_library(
25    patterns: &[String],
26    validation_level: PatternValidationLevel,
27) -> anyhow::Result<()> {
28    if validation_level == PatternValidationLevel::None {
29        return Ok(());
30    }
31
32    let issues = ignore::validate_ignore_patterns(patterns);
33    let has_errors = issues.iter().any(|i| i.severity == PatternSeverity::Error);
34    let has_warnings = issues
35        .iter()
36        .any(|i| i.severity == PatternSeverity::Warning);
37
38    if has_errors || (validation_level == PatternValidationLevel::Strict && has_warnings) {
39        // For library usage, we collect all issues into the error message
40        let error_messages: Vec<String> = issues
41            .iter()
42            .filter(|issue| {
43                issue.severity == PatternSeverity::Error
44                    || (validation_level == PatternValidationLevel::Strict
45                        && issue.severity == PatternSeverity::Warning)
46            })
47            .map(|issue| format!("{}: {}", issue.pattern, issue.message))
48            .collect();
49
50        bail!("Pattern validation failed: {}", error_messages.join("; "));
51    }
52
53    Ok(())
54}
55
56/// Pattern validation severity levels
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub enum PatternSeverity {
59    /// Informational message
60    Info,
61    /// Warning about potentially problematic pattern
62    Warning,
63    /// Error that prevents pattern from being added
64    Error,
65}
66
67/// A pattern validation issue
68#[derive(Debug, Clone)]
69pub struct PatternIssue {
70    pub pattern: String,
71    pub severity: PatternSeverity,
72    pub message: String,
73}
74
75/// Pattern validation level
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub enum PatternValidationLevel {
78    /// Skip all validation
79    None,
80    /// Show warnings and errors, but only fail on errors
81    Warn,
82    /// Show all issues and fail on any issue
83    Strict,
84}
85
86/// Add patterns to repository .gitignore file
87pub fn add_patterns_to_gitignore(
88    patterns: &[String],
89    validation_level: PatternValidationLevel,
90) -> anyhow::Result<Vec<String>> {
91    validate_patterns_for_library(patterns, validation_level)?;
92    let gitignore_path = git::get_gitignore_path()?;
93    ignore::add_patterns_to_ignore_file(
94        &gitignore_path,
95        patterns,
96        true,
97        PatternValidationLevel::None,
98    )
99}
100
101/// Add patterns to local .git/info/exclude file
102pub fn add_patterns_to_exclude(
103    patterns: &[String],
104    validation_level: PatternValidationLevel,
105) -> anyhow::Result<Vec<String>> {
106    validate_patterns_for_library(patterns, validation_level)?;
107    let exclude_path = git::get_exclude_file_path()?;
108    ignore::ensure_info_exclude_exists(&exclude_path)?;
109    ignore::add_patterns_to_ignore_file(&exclude_path, patterns, true, PatternValidationLevel::None)
110}
111
112/// Add patterns to global gitignore file
113pub fn add_patterns_to_global(
114    patterns: &[String],
115    validation_level: PatternValidationLevel,
116) -> anyhow::Result<Vec<String>> {
117    validate_patterns_for_library(patterns, validation_level)?;
118    let global_path = git::get_global_gitignore_path()
119        .ok_or_else(|| anyhow::anyhow!("No global gitignore file configured"))?;
120    ignore::add_patterns_to_ignore_file(&global_path, patterns, true, PatternValidationLevel::None)
121}