gitoxide_core/repository/
config.rs1use anyhow::{bail, Result};
2use gix::{bstr::BString, config::AsKey};
3
4use crate::OutputFormat;
5
6pub fn list(
7 repo: gix::Repository,
8 filters: Vec<BString>,
9 overrides: Vec<BString>,
10 format: OutputFormat,
11 mut out: impl std::io::Write,
12) -> Result<()> {
13 if format != OutputFormat::Human {
14 bail!("Only human output format is supported at the moment");
15 }
16 let repo = gix::open_opts(repo.git_dir(), repo.open_options().clone().cli_overrides(overrides))?;
17 let config = repo.config_snapshot();
18 if let Some(frontmatter) = config.frontmatter() {
19 for event in frontmatter {
20 event.write_to(&mut out)?;
21 }
22 }
23 let filters: Vec<_> = filters.into_iter().map(Filter::new).collect();
24 let mut last_meta = None;
25 let mut it = config.sections_and_postmatter().peekable();
26 while let Some((section, matter)) = it.next() {
27 if !filters.is_empty() && !filters.iter().any(|filter| filter.matches_section(section)) {
28 continue;
29 }
30
31 let meta = section.meta();
32 if last_meta != Some(meta) {
33 write_meta(meta, &mut out)?;
34 }
35 last_meta = Some(meta);
36
37 section.write_to(&mut out)?;
38 for event in matter {
39 event.write_to(&mut out)?;
40 }
41 if it
42 .peek()
43 .is_some_and(|(next_section, _)| next_section.header().name() != section.header().name())
44 {
45 writeln!(&mut out)?;
46 }
47 }
48 Ok(())
49}
50
51struct Filter {
52 name: String,
53 subsection: Option<BString>,
54}
55
56impl Filter {
57 fn new(input: BString) -> Self {
58 match input.try_as_key() {
59 Some(key) => Filter {
60 name: key.section_name.into(),
61 subsection: key.subsection_name.map(ToOwned::to_owned),
62 },
63 None => Filter {
64 name: input.to_string(),
65 subsection: None,
66 },
67 }
68 }
69
70 fn matches_section(&self, section: &gix::config::file::Section<'_>) -> bool {
71 let ignore_case = gix::glob::wildmatch::Mode::IGNORE_CASE;
72
73 if !gix::glob::wildmatch(self.name.as_bytes().into(), section.header().name(), ignore_case) {
74 return false;
75 }
76 match (self.subsection.as_deref(), section.header().subsection_name()) {
77 (Some(filter), Some(name)) => {
78 if !gix::glob::wildmatch(filter.as_slice().into(), name, ignore_case) {
79 return false;
80 }
81 }
82 (None, _) => {}
83 (Some(_), None) => return false,
84 }
85 true
86 }
87}
88
89fn write_meta(meta: &gix::config::file::Metadata, out: &mut impl std::io::Write) -> std::io::Result<()> {
90 writeln!(
91 out,
92 "# From '{}' ({:?}{}{})",
93 meta.path
94 .as_deref()
95 .map_or_else(|| "memory".into(), |p| p.display().to_string()),
96 meta.source,
97 (meta.level != 0)
98 .then(|| format!(", include level {}", meta.level))
99 .unwrap_or_default(),
100 (meta.trust != gix::sec::Trust::Full)
101 .then_some(", untrusted")
102 .unwrap_or_default()
103 )
104}