146 lines · 5.1 KB · Rust
RawDownload
use gix::worktree::IndexPersistedOrInMemory; use crate::OutputFormat; pub struct Options {    pub format: OutputFormat,    pub statistics: bool,} pub(crate) mod function {    use std::{borrow::Cow, io, path::Path};     use anyhow::bail;    use gix::bstr::BStr;     use crate::{        OutputFormat, is_dir_to_mode,        repository::{            PathsOrPatterns,            attributes::query::{Options, attributes_cache},        },    };     pub fn query(        repo: gix::Repository,        input: PathsOrPatterns,        mut out: impl io::Write,        mut err: impl io::Write,        Options { format, statistics }: Options,    ) -> anyhow::Result<()> {        if format != OutputFormat::Human {            bail!("JSON output isn't implemented yet");        }         let (mut cache, index) = attributes_cache(&repo)?;        let mut matches = cache.attribute_matches();         match input {            PathsOrPatterns::Paths(paths) => {                for path in paths {                    let mode = gix::path::from_bstr(Cow::Borrowed(path.as_ref()))                        .metadata()                        .ok()                        .map(|m| is_dir_to_mode(m.is_dir()));                     let entry = cache.at_entry(&path, mode)?;                    if !entry.matching_attributes(&mut matches) {                        continue;                    }                    print_match(&matches, path.as_ref(), &mut out)?;                }            }            PathsOrPatterns::Patterns(patterns) => {                let mut pathspec = repo.pathspec(                    true,                    patterns.iter(),                    true,                    &index,                    gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping                        .adjust_for_bare(repo.is_bare()),                )?;                let mut pathspec_matched_entry = false;                if let Some(it) = pathspec.index_entries_with_paths(&index) {                    for (path, entry) in it {                        pathspec_matched_entry = true;                        let entry = cache.at_entry(path, entry.mode.into())?;                        if !entry.matching_attributes(&mut matches) {                            continue;                        }                        print_match(&matches, path, &mut out)?;                    }                }                 if !pathspec_matched_entry {                    // TODO(borrowchk): this shouldn't be necessary at all, but `pathspec` stays borrowed mutably for some reason.                    //                  It's probably due to the strange lifetimes of `index_entries_with_paths()`.                    let pathspec = repo.pathspec(                        true,                        patterns.iter(),                        true,                        &index,                        gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping                            .adjust_for_bare(repo.is_bare()),                    )?;                    let workdir = repo.workdir();                    for pattern in pathspec.search().patterns() {                        let path = pattern.path();                        let entry = cache.at_entry(                            path,                            Some(is_dir_to_mode(                                workdir.is_some_and(|wd| wd.join(gix::path::from_bstr(path)).is_dir())                                    || pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR),                            )),                        )?;                        if !entry.matching_attributes(&mut matches) {                            continue;                        }                        print_match(&matches, path, &mut out)?;                    }                }            }        }         if let Some(stats) = statistics.then(|| cache.take_statistics()) {            out.flush()?;            writeln!(err, "{stats:#?}").ok();        }        Ok(())    }     fn print_match(        matches: &gix::attrs::search::Outcome,        path: &BStr,        mut out: impl std::io::Write,    ) -> std::io::Result<()> {        for m in matches.iter() {            writeln!(                out,                "{}:{}:{}\t{}\t{}",                m.location.source.map(Path::to_string_lossy).unwrap_or_default(),                m.location.sequence_number,                m.pattern,                path,                m.assignment            )?;        }        Ok(())    }} pub(crate) fn attributes_cache(    repo: &gix::Repository,) -> anyhow::Result<(gix::AttributeStack<'_>, IndexPersistedOrInMemory)> {    let index = repo.index_or_load_from_head()?;    let cache = repo.attributes(        &index,        if repo.is_bare() {            gix::worktree::stack::state::attributes::Source::IdMapping        } else {            gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping        },        gix::worktree::stack::state::ignore::Source::IdMapping,        None,    )?;    Ok((cache, index))}