eenv 0.1.1

Encrypted Env Manager: encrypts .env files, manages examples, and enforces safety via git hooks.
Documentation
use crate::config::validate_eenv_config;
use crate::types::EenvState;
use ignore::{DirEntry, WalkBuilder};
use std::{io, path::Path, path::PathBuf};

pub fn find_env_files_recursive(root: &Path) -> io::Result<Vec<PathBuf>> {
    let mut builder = WalkBuilder::new(root);
    builder
        .hidden(true)
        .follow_links(false)
        .standard_filters(false)
        .parents(false)
        .add_custom_ignore_filename(".eenvignore")
        .filter_entry(|d| d.depth() == 0 || true);

    let mut out = Vec::new();
    for result in builder.build() {
        let dent = match result {
            Ok(d) => d,
            Err(err) => {
                eprintln!("walk error: {err}");
                continue;
            }
        };
        if is_env_file(&dent) {
            let abs = dent
                .path()
                .canonicalize()
                .unwrap_or_else(|_| dent.path().to_path_buf());
            out.push(abs);
        }
    }
    out.sort();
    out.dedup();
    Ok(out)
}

pub fn split_env_files(mut files: Vec<PathBuf>) -> (Vec<PathBuf>, Vec<PathBuf>, Vec<PathBuf>) {
    files.sort();
    files.dedup();
    let mut real = Vec::new();
    let mut examples = Vec::new();
    let mut encs = Vec::new();
    for path in files {
        if let Some(name) = path.file_name().and_then(|s| s.to_str()) {
            if name.ends_with(".example") {
                examples.push(path);
            } else if name.ends_with(".enc") {
                encs.push(path);
            } else {
                real.push(path);
            }
        }
    }
    (real, examples, encs)
}

fn is_env_file(d: &DirEntry) -> bool {
    if !d.file_type().map(|t| t.is_file()).unwrap_or(false) {
        return false;
    }
    matches!(d.path().file_name().and_then(|s| s.to_str()), Some(name) if name.starts_with(".env"))
}

pub fn compute_eenv_state(repo_root: &Path) -> io::Result<EenvState> {
    let files = find_env_files_recursive(repo_root)?;
    let (real, examples, encs) = split_env_files(files);
    let enc = !encs.is_empty();
    let example = !examples.is_empty();
    let env = !real.is_empty();
    let eenvjson = validate_eenv_config(repo_root)?;
    Ok(EenvState {
        enc,
        example,
        env,
        eenvjson,
    })
}