Skip to content

Commit 4a443e4

Browse files
committed
feat!: make API less error prone by enforcing overrides at instantiation time.
It's made so that overrides can still be applied at a later point.
1 parent 8172f0e commit 4a443e4

File tree

4 files changed

+38
-9
lines changed

4 files changed

+38
-9
lines changed

gix-submodule/src/access.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::config::{Branch, FetchRecurse, Ignore, Update};
22
use crate::{config, File};
33
use bstr::BStr;
44
use std::borrow::Cow;
5+
use std::collections::HashSet;
56
use std::path::Path;
67

78
/// High-Level Access
@@ -26,11 +27,17 @@ impl File {
2627
///
2728
/// Note that these exact names have to be used for querying submodule values.
2829
pub fn names(&self) -> impl Iterator<Item = &BStr> {
30+
let mut seen = HashSet::<&BStr>::default();
2931
self.config
3032
.sections_by_name("submodule")
3133
.into_iter()
3234
.flatten()
33-
.filter_map(|s| s.header().subsection_name())
35+
.filter_map(move |s| {
36+
s.header()
37+
.subsection_name()
38+
.filter(|_| s.meta().source == crate::init::META_MARKER)
39+
.filter(|name| seen.insert(*name))
40+
})
3441
}
3542

3643
/// Return an iterator of names along with a boolean that indicates the submodule is active (`true`) or inactive (`false`).

gix-submodule/src/lib.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,41 @@ mod init {
9090
}
9191
}
9292

93+
/// A marker we use when listing names to not pick them up from overridden sections.
94+
pub(crate) const META_MARKER: gix_config::Source = gix_config::Source::Api;
95+
9396
/// Lifecycle
9497
impl File {
9598
/// Parse `bytes` as git configuration, typically from `.gitmodules`, without doing any further validation.
9699
/// `path` can be provided to keep track of where the file was read from in the underlying [`config`](Self::config())
97100
/// instance.
101+
/// `config` is used to [apply value overrides](File::append_submodule_overrides), which can be empty if overrides
102+
/// should be applied at a later time.
98103
///
99104
/// Future access to the module information is lazy and configuration errors are exposed there on a per-value basis.
100105
///
101106
/// ### Security Considerations
102107
///
103108
/// The information itself should be used with care as it can direct the caller to fetch from remotes. It is, however,
104109
/// on the caller to assure the input data can be trusted.
105-
pub fn from_bytes(bytes: &[u8], path: impl Into<Option<PathBuf>>) -> Result<Self, gix_config::parse::Error> {
106-
let metadata = path.into().map_or_else(gix_config::file::Metadata::api, |path| {
107-
gix_config::file::Metadata::from(gix_config::Source::Worktree).at(path)
108-
});
109-
let config = gix_config::File::from_parse_events_no_includes(
110+
pub fn from_bytes(
111+
bytes: &[u8],
112+
path: impl Into<Option<PathBuf>>,
113+
config: &gix_config::File<'_>,
114+
) -> Result<Self, gix_config::parse::Error> {
115+
let metadata = {
116+
let mut meta = gix_config::file::Metadata::from(META_MARKER);
117+
meta.path = path.into();
118+
meta
119+
};
120+
let modules = gix_config::File::from_parse_events_no_includes(
110121
gix_config::parse::Events::from_bytes_owned(bytes, None)?,
111122
metadata,
112123
);
113124

114-
Ok(Self { config })
125+
let mut res = Self { config: modules };
126+
res.append_submodule_overrides(config);
127+
Ok(res)
115128
}
116129

117130
/// Turn ourselves into the underlying parsed configuration file.

gix-submodule/tests/file/baseline.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use std::path::PathBuf;
66
#[test]
77
fn common_values_and_names_by_path() -> crate::Result {
88
let modules = module_files()
9-
.map(|(path, stripped)| gix_submodule::File::from_bytes(&std::fs::read(path).unwrap(), stripped))
9+
.map(|(path, stripped)| {
10+
gix_submodule::File::from_bytes(&std::fs::read(path).unwrap(), stripped, &Default::default())
11+
})
1012
.collect::<Result<Vec<_>, _>>()?;
1113

1214
assert_eq!(

gix-submodule/tests/file/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fn submodule(bytes: &str) -> gix_submodule::File {
2-
gix_submodule::File::from_bytes(bytes.as_bytes(), None).expect("valid module")
2+
gix_submodule::File::from_bytes(bytes.as_bytes(), None, &Default::default()).expect("valid module")
33
}
44

55
mod names_and_active_state {
@@ -13,6 +13,7 @@ mod names_and_active_state {
1313
Ok(gix_submodule::File::from_bytes(
1414
std::fs::read(&modules)?.as_slice(),
1515
modules,
16+
&Default::default(),
1617
)?)
1718
}
1819

@@ -213,13 +214,19 @@ mod update {
213214
fn valid_in_overrides() -> crate::Result {
214215
let mut module = submodule("[submodule.a]\n update = merge");
215216
let repo_config = gix_config::File::from_str("[submodule.a]\n update = !dangerous")?;
217+
let prev_names = module.names().map(ToOwned::to_owned).collect::<Vec<_>>();
216218
module.append_submodule_overrides(&repo_config);
217219

218220
assert_eq!(
219221
module.update("a".into())?.expect("present"),
220222
Update::Command("dangerous".into()),
221223
"overridden values are picked up and make commands possible - these are local"
222224
);
225+
assert_eq!(
226+
module.names().map(ToOwned::to_owned).collect::<Vec<_>>(),
227+
prev_names,
228+
"Appending more configuration sections doesn't affect name listing"
229+
);
223230
Ok(())
224231
}
225232

0 commit comments

Comments
 (0)