//! Primitives for describing git submodules.#![deny(missing_docs)]#![forbid(unsafe_code)] use std::collections::BTreeMap; use bstr::ByteSlice; /// All relevant information about a git module, typically from `.gitmodules` files.////// Note that overrides from other configuration might be relevant, which is why this type/// can be used to take these into consideration when presented with other configuration/// from the superproject.#[derive(Clone)]pub struct File { config: gix_config::File,} mod access; ///pub mod config; ///pub mod is_active_platform; /// A platform to keep the state necessary to perform repeated active checks, created by [File::is_active_platform()].pub struct IsActivePlatform { pub(crate) search: Option<gix_pathspec::Search>,} /// Mutationimpl File { /// This can be used to let `config` override some values we know about submodules, namely… /// /// * `url` /// * `fetchRecurseSubmodules` /// * `ignore` /// * `update` /// * `branch` /// /// These values aren't validated yet, which will happen upon query. pub fn append_submodule_overrides( &mut self, config: &gix_config::File, ) -> Result<&mut Self, gix_config::file::section::value::Error> { let mut values = BTreeMap::<_, Vec<_>>::new(); for (module_name, section) in config .sections_by_name("submodule") .into_iter() .flatten() .filter_map(|s| s.header().subsection_name().map(|n| (n, s))) { for field in ["url", "fetchRecurseSubmodules", "ignore", "update", "branch"] { if let Some(value) = section.value(field) { values.entry((module_name, field)).or_default().push(value); } } } let values = { let mut v: Vec<_> = values.into_iter().collect(); v.sort_by_key(|a| a.0.0); v }; let mut config_to_append = gix_config::File::new(config.meta_owned()); let mut prev_name = None; for ((module_name, field), values) in values { if prev_name != Some(module_name) { config_to_append .new_section("submodule", module_name) .expect("all names come from valid configuration, so remain valid"); prev_name = Some(module_name); } config_to_append .section_mut("submodule", Some(module_name)) .expect("always set at this point") .push( field, Some( values .last() .expect("at least one value or we wouldn't be here") .as_bstr(), ), )?; } self.config.append(config_to_append)?; Ok(self) }} ///pub mod init { use std::path::PathBuf; use crate::File; impl std::fmt::Debug for File { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("File") .field("config_path", &self.config_path()) .field("config", &format_args!("r#\"{}\"#", self.config)) .finish() } } /// A marker we use when listing names to not pick them up from overridden sections. pub(crate) const META_MARKER: gix_config::Source = gix_config::Source::Api; /// Lifecycle /// The error returned when parsing a submodule configuration file. #[derive(Debug, thiserror::Error)] pub enum Error { /// The configuration could not be parsed. #[error(transparent)] Parse(#[from] gix_config::parse::Error), /// Applying configuration overrides exceeded the supported span size. #[error(transparent)] Span(#[from] gix_config::parse::span::Error), /// Applying configuration overrides failed. #[error(transparent)] SectionValue(#[from] gix_config::file::section::value::Error), } impl File { /// Parse `bytes` as git configuration, typically from `.gitmodules`, without doing any further validation. /// `path` can be provided to keep track of where the file was read from in the underlying [`config`](Self::config()) /// instance. /// `config` is used to [apply value overrides](File::append_submodule_overrides), which can be empty if overrides /// should be applied at a later time. /// /// Future access to the module information is lazy and configuration errors are exposed there on a per-value basis. /// /// ### Security Considerations /// /// The information itself should be used with care as it can direct the caller to fetch from remotes. It is, however, /// on the caller to assure the input data can be trusted. pub fn from_bytes( bytes: &[u8], path: impl Into<Option<PathBuf>>, config: &gix_config::File, ) -> Result<Self, Error> { let metadata = { let mut meta = gix_config::file::Metadata::from(META_MARKER); meta.path = path.into(); meta }; let modules = gix_config::File::from_parse_events_no_includes( gix_config::parse::Events::from_bytes(bytes, None)?, metadata, ); let mut res = Self { config: modules }; res.append_submodule_overrides(config)?; Ok(res) } /// Turn ourselves into the underlying parsed configuration file. pub fn into_config(self) -> gix_config::File { self.config } }}
//! Primitives for describing git submodules.
#![deny(missing_docs)]
#![forbid(unsafe_code)]
use std::collections::BTreeMap;
use bstr::ByteSlice;
/// All relevant information about a git module, typically from `.gitmodules` files.
///
/// Note that overrides from other configuration might be relevant, which is why this type
/// can be used to take these into consideration when presented with other configuration
/// from the superproject.
#[derive(Clone)]
pub struct File {
config: gix_config::File,
}
mod access;
///
pub mod config;
///
pub mod is_active_platform;
/// A platform to keep the state necessary to perform repeated active checks, created by [File::is_active_platform()].
pub struct IsActivePlatform {
pub(crate) search: Option<gix_pathspec::Search>,
}
/// Mutation
impl File {
/// This can be used to let `config` override some values we know about submodules, namely…
///
/// * `url`
/// * `fetchRecurseSubmodules`
/// * `ignore`
/// * `update`
/// * `branch`
///
/// These values aren't validated yet, which will happen upon query.
pub fn append_submodule_overrides(
&mut self,
config: &gix_config::File,
) -> Result<&mut Self, gix_config::file::section::value::Error> {
let mut values = BTreeMap::<_, Vec<_>>::new();
for (module_name, section) in config
.sections_by_name("submodule")
.into_iter()
.flatten()
.filter_map(|s| s.header().subsection_name().map(|n| (n, s)))
{
for field in ["url", "fetchRecurseSubmodules", "ignore", "update", "branch"] {
if let Some(value) = section.value(field) {
values.entry((module_name, field)).or_default().push(value);
}
}
}
let values = {
let mut v: Vec<_> = values.into_iter().collect();
v.sort_by_key(|a| a.0.0);
v
};
let mut config_to_append = gix_config::File::new(config.meta_owned());
let mut prev_name = None;
for ((module_name, field), values) in values {
if prev_name != Some(module_name) {
config_to_append
.new_section("submodule", module_name)
.expect("all names come from valid configuration, so remain valid");
prev_name = Some(module_name);
}
config_to_append
.section_mut("submodule", Some(module_name))
.expect("always set at this point")
.push(
field,
Some(
values
.last()
.expect("at least one value or we wouldn't be here")
.as_bstr(),
),
)?;
}
self.config.append(config_to_append)?;
Ok(self)
}
}
///
pub mod init {
use std::path::PathBuf;
use crate::File;
impl std::fmt::Debug for File {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("File")
.field("config_path", &self.config_path())
.field("config", &format_args!("r#\"{}\"#", self.config))
.finish()
}
}
/// A marker we use when listing names to not pick them up from overridden sections.
pub(crate) const META_MARKER: gix_config::Source = gix_config::Source::Api;
/// Lifecycle
/// The error returned when parsing a submodule configuration file.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// The configuration could not be parsed.
#[error(transparent)]
Parse(#[from] gix_config::parse::Error),
/// Applying configuration overrides exceeded the supported span size.
#[error(transparent)]
Span(#[from] gix_config::parse::span::Error),
/// Applying configuration overrides failed.
#[error(transparent)]
SectionValue(#[from] gix_config::file::section::value::Error),
}
impl File {
/// Parse `bytes` as git configuration, typically from `.gitmodules`, without doing any further validation.
/// `path` can be provided to keep track of where the file was read from in the underlying [`config`](Self::config())
/// instance.
/// `config` is used to [apply value overrides](File::append_submodule_overrides), which can be empty if overrides
/// should be applied at a later time.
///
/// Future access to the module information is lazy and configuration errors are exposed there on a per-value basis.
///
/// ### Security Considerations
///
/// The information itself should be used with care as it can direct the caller to fetch from remotes. It is, however,
/// on the caller to assure the input data can be trusted.
pub fn from_bytes(
bytes: &[u8],
path: impl Into<Option<PathBuf>>,
config: &gix_config::File,
) -> Result<Self, Error> {
let metadata = {
let mut meta = gix_config::file::Metadata::from(META_MARKER);
meta.path = path.into();
meta
};
let modules = gix_config::File::from_parse_events_no_includes(
gix_config::parse::Events::from_bytes(bytes, None)?,
metadata,
);
let mut res = Self { config: modules };
res.append_submodule_overrides(config)?;
Ok(res)
}
/// Turn ourselves into the underlying parsed configuration file.
pub fn into_config(self) -> gix_config::File {
self.config
}
}
}