use std::{
collections::HashSet,
fs::{File, OpenOptions, read_to_string},
io::Write,
path::Path,
};
use crate::{
errors::Result,
git::{COMMIT_MESSAGE_FILE_PATH, find_git_root, get_top_level_path},
};
const COMMITIGNORE_FILE_PATH: &str = ".commitignore";
const GITIGNORE_FILE_PATH: &str = ".gitignore";
pub fn add_to_git_exclude(paths: &[&str]) -> Result<()> {
let git_root_path = find_git_root()?;
let info_dir = git_root_path.join("info");
let exclude_file = info_dir.join("exclude");
if !info_dir.exists() {
std::fs::create_dir_all(&info_dir)?;
}
let content = if exclude_file.exists() {
read_to_string(&exclude_file)?
} else {
String::new()
};
let existing_paths: HashSet<&str> = content
.lines()
.filter(|line| !line.starts_with('#') && !line.trim().is_empty())
.collect();
let paths_to_add: Vec<&str> = paths
.iter()
.filter(|path| !existing_paths.contains(*path))
.copied()
.collect();
if paths_to_add.is_empty() {
return Ok(());
}
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(exclude_file)?;
if !content.contains("# Added by git-commit-rust") {
if !content.is_empty() {
writeln!(file)?;
}
writeln!(file, "# Added by git-commit-rust")?;
}
for path in paths_to_add {
writeln!(file, "{path}")?;
}
Ok(())
}
pub fn create_needed_files() -> Result<()> {
let project_root = get_top_level_path()?;
let commit_file_path = Path::new(&project_root).join(COMMIT_MESSAGE_FILE_PATH);
let commitignore_file_path = Path::new(&project_root).join(COMMITIGNORE_FILE_PATH);
if !commit_file_path.exists() {
File::create(commit_file_path)?;
}
if !commitignore_file_path.exists() {
File::create(commitignore_file_path)?;
}
add_to_git_exclude(&[COMMIT_MESSAGE_FILE_PATH, COMMITIGNORE_FILE_PATH])?;
Ok(())
}
pub fn get_ignore_patterns() -> Result<Vec<String>> {
let commitignore_path = Path::new(COMMITIGNORE_FILE_PATH);
if !commitignore_path.exists() {
return Ok(Vec::new());
}
let mut patterns = process_gitignore_file()?;
patterns.append(&mut process_gitignore_file()?);
Ok(patterns)
}
pub fn process_gitignore_file() -> Result<Vec<String>> {
let gitignore_file_path = Path::new(GITIGNORE_FILE_PATH);
if !gitignore_file_path.exists() {
return Ok(Vec::new());
}
let git_ignore_file_contents = read_to_string(gitignore_file_path)?;
extract_filenames(&git_ignore_file_contents, r"^([^#]\S*)$")
}
use super::extract_filenames;