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