From eda39ec7d736d49af1ad9e2ad775e4aa12b264b7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 21 Jul 2022 11:04:17 +0800 Subject: [PATCH] feat: `gix config` with section and sub-section filtering. (#331) --- gitoxide-core/src/repository/config.rs | 85 +++++++++++++++++++++++++- src/plumbing/options.rs | 5 +- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/gitoxide-core/src/repository/config.rs b/gitoxide-core/src/repository/config.rs index 60f58632540..b9654a03e30 100644 --- a/gitoxide-core/src/repository/config.rs +++ b/gitoxide-core/src/repository/config.rs @@ -4,15 +4,94 @@ use git_repository as git; pub fn list( repo: git::Repository, - _filters: Vec, + filters: Vec, format: OutputFormat, - out: impl std::io::Write, + mut out: impl std::io::Write, ) -> Result<()> { if format != OutputFormat::Human { bail!("Only human output format is supported at the moment"); } let config = repo.config_snapshot(); let config = config.plumbing(); - config.write_to(out)?; + if let Some(frontmatter) = config.frontmatter() { + for event in frontmatter { + event.write_to(&mut out)?; + } + } + let filters: Vec<_> = filters.into_iter().map(Filter::new).collect(); + let mut last_meta = None; + for (section, matter) in config.sections_and_postmatter() { + if !filters.is_empty() && !filters.iter().any(|filter| filter.matches_section(section)) { + continue; + } + + let meta = section.meta(); + if last_meta.map_or(true, |last| last != meta) { + write_meta(meta, &mut out)?; + } + last_meta = Some(meta); + + section.write_to(&mut out)?; + for event in matter { + event.write_to(&mut out)?; + } + writeln!(&mut out)?; + } Ok(()) } + +struct Filter { + name: String, + subsection: Option, +} + +impl Filter { + fn new(input: String) -> Self { + match git::config::parse::key(&input) { + Some(key) => Filter { + name: key.section_name.into(), + subsection: key.subsection_name.map(ToOwned::to_owned), + }, + None => Filter { + name: input, + subsection: None, + }, + } + } + + fn matches_section(&self, section: &git::config::file::Section<'_>) -> bool { + let ignore_case = git::glob::wildmatch::Mode::IGNORE_CASE; + + if !git::glob::wildmatch(self.name.as_bytes().into(), section.header().name(), ignore_case) { + return false; + } + match (self.subsection.as_deref(), section.header().subsection_name()) { + (Some(filter), Some(name)) => { + if !git::glob::wildmatch(filter.as_bytes().into(), name, ignore_case) { + return false; + } + } + (None, None) | (None, Some(_)) => {} + _ => return false, + }; + true + } +} + +fn write_meta(meta: &git::config::file::Metadata, out: &mut impl std::io::Write) -> std::io::Result<()> { + writeln!( + out, + "# From '{}' ({:?}{}{})", + meta.path + .as_deref() + .map(|p| p.display().to_string()) + .unwrap_or_else(|| "memory".into()), + meta.source, + (meta.level != 0) + .then(|| format!(", include level {}", meta.level)) + .unwrap_or_default(), + (meta.trust != git::sec::Trust::Full) + .then(|| "untrusted") + .unwrap_or_default() + ) +} diff --git a/src/plumbing/options.rs b/src/plumbing/options.rs index 9c49b160544..abed5255313 100644 --- a/src/plumbing/options.rs +++ b/src/plumbing/options.rs @@ -85,7 +85,10 @@ pub mod config { #[derive(Debug, clap::Parser)] #[clap(subcommand_required(false))] pub struct Platform { - /// The filter terms to limit the output to matching sections and values only. + /// The filter terms to limit the output to matching sections and subsections only. + /// + /// Typical filters are `branch` or `remote.origin` or `remote.or*` - git-style globs are supported + /// and comparisons are case-insensitive. pub filter: Vec, } }