Skip to content

Commit 3503f49

Browse files
committed
feat!: remove File::names_and_active_state() in favor of File::is_active_platform().
With this platform it's possible to make repeated checks to see if a named submodule is active.
1 parent 4a443e4 commit 3503f49

File tree

5 files changed

+113
-66
lines changed

5 files changed

+113
-66
lines changed

gix-submodule/src/access.rs

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::config::{Branch, FetchRecurse, Ignore, Update};
2-
use crate::{config, File};
2+
use crate::{config, File, IsActivePlatform};
33
use bstr::BStr;
44
use std::borrow::Cow;
55
use std::collections::HashSet;
@@ -40,17 +40,7 @@ impl File {
4040
})
4141
}
4242

43-
/// Return an iterator of names along with a boolean that indicates the submodule is active (`true`) or inactive (`false`).
44-
/// If the boolean was wrapped in an error, there was a configuration error.
45-
/// Use `defaults` for parsing the pathspecs used to match on names via `submodule.active` configuration retrieved from `config`.
46-
/// `attributes` provides a way to resolve the attributes mentioned in pathspecs.
47-
///
48-
/// Inactive submodules should not participate in any operations that are applying to all submodules.
49-
///
50-
/// Note that the entirety of sections in `config` are considered, not just the ones of the configuration for the repository itself.
51-
/// `submodule.active` pathspecs are considered to be top-level specs and match the name of submodules, which may be considered active
52-
/// on match. However, there is a [hierarchy of rules](https://git-scm.com/docs/gitsubmodules#_active_submodules) that's
53-
/// implemented here, but pathspecs add the most complexity.
43+
/// Similar to [Self::is_active_platform()], but automatically applies it to each name to learn if a submodule is active or not.
5444
pub fn names_and_active_state<'a>(
5545
&'a self,
5646
config: &'a gix_config::File<'static>,
@@ -63,12 +53,30 @@ impl File {
6353
) -> bool
6454
+ 'a,
6555
) -> Result<
66-
impl Iterator<Item = (&BStr, Result<bool, config::names_and_active_state::iter::Error>)> + 'a,
67-
config::names_and_active_state::Error,
56+
impl Iterator<Item = (&BStr, Result<bool, crate::is_active_platform::is_active::Error>)> + 'a,
57+
crate::is_active_platform::Error,
6858
> {
69-
let mut search = config
59+
let mut platform = self.is_active_platform(config, defaults)?;
60+
let iter = self
61+
.names()
62+
.map(move |name| (name, platform.is_active(self, config, name, &mut attributes)));
63+
Ok(iter)
64+
}
65+
66+
/// Return a platform which allows to check if a submodule name is active or inactive.
67+
/// Use `defaults` for parsing the pathspecs used to later match on names via `submodule.active` configuration retrieved from `config`.
68+
///
69+
/// All `submodule.active` pathspecs are considered to be top-level specs and match the name of submodules, which are active
70+
/// on inclusive match.
71+
/// The full algorithm is described as [hierarchy of rules](https://git-scm.com/docs/gitsubmodules#_active_submodules).
72+
pub fn is_active_platform(
73+
&self,
74+
config: &gix_config::File<'_>,
75+
defaults: gix_pathspec::Defaults,
76+
) -> Result<IsActivePlatform, crate::is_active_platform::Error> {
77+
let search = config
7078
.strings_by_key("submodule.active")
71-
.map(|patterns| -> Result<_, config::names_and_active_state::Error> {
79+
.map(|patterns| -> Result<_, crate::is_active_platform::Error> {
7280
let patterns = patterns
7381
.into_iter()
7482
.map(|pattern| gix_pathspec::parse(&pattern, defaults))
@@ -80,27 +88,7 @@ impl File {
8088
)?)
8189
})
8290
.transpose()?;
83-
let iter = self.names().map(move |name| {
84-
let active = (|| -> Result<_, config::names_and_active_state::iter::Error> {
85-
if let Some(val) = config.boolean("submodule", Some(name), "active").transpose()? {
86-
return Ok(val);
87-
};
88-
if let Some(val) = search
89-
.as_mut()
90-
.and_then(|search| search.pattern_matching_relative_path(name, Some(true), &mut attributes))
91-
.map(|m| !m.is_excluded())
92-
{
93-
return Ok(val);
94-
}
95-
Ok(match self.url(name) {
96-
Ok(_) => true,
97-
Err(config::url::Error::Missing { .. }) => false,
98-
Err(err) => return Err(err.into()),
99-
})
100-
})();
101-
(name, active)
102-
});
103-
Ok(iter)
91+
Ok(IsActivePlatform { search })
10492
}
10593

10694
/// Given the `relative_path` (as seen from the root of the worktree) of a submodule with possibly platform-specific

gix-submodule/src/config.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -214,28 +214,3 @@ pub mod path {
214214
OutsideOfWorktree { actual: BString, submodule: BString },
215215
}
216216
}
217-
///
218-
pub mod names_and_active_state {
219-
/// The error returned by [File::names_and_active_state](crate::File::names_and_active_state()).
220-
#[derive(Debug, thiserror::Error)]
221-
#[allow(missing_docs)]
222-
pub enum Error {
223-
#[error(transparent)]
224-
NormalizePattern(#[from] gix_pathspec::normalize::Error),
225-
#[error(transparent)]
226-
ParsePattern(#[from] gix_pathspec::parse::Error),
227-
}
228-
229-
///
230-
pub mod iter {
231-
/// The error returned by the iterator of [File::names_and_active_state](crate::File::names_and_active_state()).
232-
#[derive(Debug, thiserror::Error)]
233-
#[allow(missing_docs)]
234-
pub enum Error {
235-
#[error("The value of the 'active' field of a submodule could not be decoded")]
236-
ActiveField(#[from] gix_config::value::Error),
237-
#[error(transparent)]
238-
Url(#[from] crate::config::url::Error),
239-
}
240-
}
241-
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::IsActivePlatform;
2+
use bstr::BStr;
3+
4+
/// The error returned by [File::names_and_active_state](crate::File::names_and_active_state()).
5+
#[derive(Debug, thiserror::Error)]
6+
#[allow(missing_docs)]
7+
pub enum Error {
8+
#[error(transparent)]
9+
NormalizePattern(#[from] gix_pathspec::normalize::Error),
10+
#[error(transparent)]
11+
ParsePattern(#[from] gix_pathspec::parse::Error),
12+
}
13+
14+
///
15+
pub mod is_active {
16+
/// The error returned by the iterator of [File::names_and_active_state](crate::File::names_and_active_state()).
17+
#[derive(Debug, thiserror::Error)]
18+
#[allow(missing_docs)]
19+
pub enum Error {
20+
#[error("The value of the 'active' field of a submodule could not be decoded")]
21+
ActiveField(#[from] gix_config::value::Error),
22+
#[error(transparent)]
23+
Url(#[from] crate::config::url::Error),
24+
}
25+
}
26+
27+
impl IsActivePlatform {
28+
/// Returns `true` if the submodule named `name` is active or `false` otherwise.
29+
/// `modules` is the instance that [is_active_platform()](crate::File::is_active_platform()) was called on, and
30+
/// `config` is the configuration that was passed there as well.
31+
/// `attributes(relative_path, case, is_dir, outcome)` provides a way to resolve the attributes mentioned in `submodule.active` pathspecs
32+
/// that are evaluated in the platforms git configuration.
33+
///
34+
/// A submodule's active state is determined in the following order
35+
///
36+
/// * it's `submodule.<name>.active` configuration is set
37+
/// * it matches a `submodule.active` pathspec either positively or negatively via `:!<spec>`
38+
/// * it's active if it has a `url`
39+
pub fn is_active(
40+
&mut self,
41+
modules: &crate::File,
42+
config: &gix_config::File<'static>,
43+
name: &BStr,
44+
attributes: impl FnMut(
45+
&BStr,
46+
gix_pathspec::attributes::glob::pattern::Case,
47+
bool,
48+
&mut gix_pathspec::attributes::search::Outcome,
49+
) -> bool,
50+
) -> Result<bool, is_active::Error> {
51+
if let Some(val) = config.boolean("submodule", Some(name), "active").transpose()? {
52+
return Ok(val);
53+
};
54+
if let Some(val) = self
55+
.search
56+
.as_mut()
57+
.and_then(|search| search.pattern_matching_relative_path(name, Some(true), attributes))
58+
.map(|m| !m.is_excluded())
59+
{
60+
return Ok(val);
61+
}
62+
Ok(match modules.url(name) {
63+
Ok(_) => true,
64+
Err(crate::config::url::Error::Missing { .. }) => false,
65+
Err(err) => return Err(err.into()),
66+
})
67+
}
68+
}

gix-submodule/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ mod access;
2121
///
2222
pub mod config;
2323

24+
///
25+
pub mod is_active_platform;
26+
27+
/// A platform to keep the state necessary to perform repeated active checks, created by [File::is_active_platform()].
28+
pub struct IsActivePlatform {
29+
pub(crate) search: Option<gix_pathspec::Search>,
30+
}
31+
2432
/// Mutation
2533
impl File {
2634
/// This can be used to let `config` override some values we know about submodules, namely…

gix-submodule/tests/file/mod.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ fn submodule(bytes: &str) -> gix_submodule::File {
22
gix_submodule::File::from_bytes(bytes.as_bytes(), None, &Default::default()).expect("valid module")
33
}
44

5-
mod names_and_active_state {
5+
mod is_active_platform {
66
use bstr::{BStr, ByteSlice};
77
use std::str::FromStr;
88

@@ -31,17 +31,25 @@ mod names_and_active_state {
3131
module: &'a gix_submodule::File,
3232
config: &'a gix_config::File<'static>,
3333
defaults: gix_pathspec::Defaults,
34-
attributes: impl FnMut(
34+
mut attributes: impl FnMut(
3535
&BStr,
3636
gix_pathspec::attributes::glob::pattern::Case,
3737
bool,
3838
&mut gix_pathspec::attributes::search::Outcome,
3939
) -> bool
4040
+ 'a,
4141
) -> crate::Result<Vec<(&'a str, bool)>> {
42+
let mut platform = module.is_active_platform(config, defaults)?;
4243
Ok(module
43-
.names_and_active_state(config, defaults, attributes)?
44-
.map(|(name, bool)| (name.to_str().expect("valid"), bool.expect("valid")))
44+
.names()
45+
.map(|name| {
46+
(
47+
name.to_str().expect("valid"),
48+
platform
49+
.is_active(module, config, name, &mut attributes)
50+
.expect("valid"),
51+
)
52+
})
4553
.collect())
4654
}
4755

0 commit comments

Comments
 (0)