Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config access uses impl Key to allow type-safe keys #1236

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion gitoxide-core/src/organize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ fn find_origin_remote(repo: &Path) -> anyhow::Result<Option<gix_url::Url>> {
let config = gix::config::File::from_path_no_includes(non_bare.as_path().into(), local)
.or_else(|_| gix::config::File::from_path_no_includes(repo.join("config"), local))?;
Ok(config
.string_by_key("remote.origin.url")
.string("remote.origin.url")
.map(|url| gix_url::Url::from_bytes(url.as_ref()))
.transpose()?)
}
Expand Down
137 changes: 65 additions & 72 deletions gix-config/src/file/access/comfort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@ use std::{borrow::Cow, convert::TryFrom};

use bstr::BStr;

use crate::{file::MetadataFilter, value, File};
use crate::{file::MetadataFilter, value, File, Key};

/// Comfortable API for accessing values
impl<'event> File<'event> {
/// Like [`value()`][File::value()], but returning `None` if the string wasn't found.
///
/// As strings perform no conversions, this will never fail.
pub fn string(
pub fn string_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
key: impl AsRef<str>,
) -> Option<Cow<'_, BStr>> {
self.string_filter(section_name, subsection_name, key, &mut |_| true)
self.string_filter_by(section_name, subsection_name, key, &mut |_| true)
bittrance marked this conversation as resolved.
Show resolved Hide resolved
}

/// Like [`string()`][File::string()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn string_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Cow<'_, BStr>> {
self.string_filter_by_key(key, &mut |_| true)
/// Like [`string()`][File::string_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn string(&self, key: impl Key) -> Option<Cow<'_, BStr>> {
self.string_filter(key, &mut |_| true)
}

/// Like [`string()`][File::string()], but the section containing the returned value must pass `filter` as well.
pub fn string_filter(
pub fn string_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand All @@ -35,14 +35,13 @@ impl<'event> File<'event> {
.ok()
}

/// Like [`string_filter()`][File::string_filter()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn string_filter_by_key<'a>(
/// Like [`string_filter()`][File::string_filter_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn string_filter(
&self,
key: impl Into<&'a BStr>,
key: impl Key,
filter: &mut MetadataFilter,
) -> Option<Cow<'_, BStr>> {
let key = crate::parse::key(key.into())?;
self.raw_value_filter(key.section_name, key.subsection_name, key.value_name, filter)
self.raw_value_filter(key.section_name(), key.subsection_name(), key.name(), filter)
.ok()
}

Expand All @@ -52,18 +51,18 @@ impl<'event> File<'event> {
/// to pose a security risk. Prefer using [`path_filter()`][File::path_filter()] instead.
///
/// As paths perform no conversions, this will never fail.
pub fn path(
pub fn path_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
key: impl AsRef<str>,
) -> Option<crate::Path<'_>> {
self.path_filter(section_name, subsection_name, key, &mut |_| true)
self.path_filter_by(section_name, subsection_name, key, &mut |_| true)
bittrance marked this conversation as resolved.
Show resolved Hide resolved
}

/// Like [`path()`][File::path()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn path_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<crate::Path<'_>> {
self.path_filter_by_key(key, &mut |_| true)
/// Like [`path()`][File::path_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn path(&self, key: impl Key) -> Option<crate::Path<'_>> {
self.path_filter(key, &mut |_| true)
}

/// Like [`path()`][File::path()], but the section containing the returned value must pass `filter` as well.
Expand All @@ -72,7 +71,7 @@ impl<'event> File<'event> {
/// locations can be
///
/// As paths perform no conversions, this will never fail.
pub fn path_filter(
pub fn path_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand All @@ -84,33 +83,32 @@ impl<'event> File<'event> {
.map(crate::Path::from)
}

/// Like [`path_filter()`][File::path_filter()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn path_filter_by_key<'a>(
/// Like [`path_filter()`][File::path_filter_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn path_filter(
&self,
key: impl Into<&'a BStr>,
key: impl Key,
filter: &mut MetadataFilter,
) -> Option<crate::Path<'_>> {
let key = crate::parse::key(key.into())?;
self.path_filter(key.section_name, key.subsection_name, key.value_name, filter)
self.path_filter_by(key.section_name(), key.subsection_name(), key.name(), filter)
}

/// Like [`value()`][File::value()], but returning `None` if the boolean value wasn't found.
pub fn boolean(
pub fn boolean_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
key: impl AsRef<str>,
) -> Option<Result<bool, value::Error>> {
self.boolean_filter(section_name, subsection_name, key, &mut |_| true)
self.boolean_filter_by(section_name, subsection_name, key, &mut |_| true)
bittrance marked this conversation as resolved.
Show resolved Hide resolved
}

/// Like [`boolean()`][File::boolean()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn boolean_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Result<bool, value::Error>> {
self.boolean_filter_by_key(key, &mut |_| true)
/// Like [`boolean()`][File::boolean_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn boolean(&self, key: impl Key) -> Option<Result<bool, value::Error>> {
self.boolean_filter(key, &mut |_| true)
}

/// Like [`boolean()`][File::boolean()], but the section containing the returned value must pass `filter` as well.
pub fn boolean_filter(
/// Like [`boolean()`][File::boolean_by()], but the section containing the returned value must pass `filter` as well.
pub fn boolean_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand All @@ -136,33 +134,32 @@ impl<'event> File<'event> {
None
}

/// Like [`boolean_filter()`][File::boolean_filter()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn boolean_filter_by_key<'a>(
/// Like [`boolean_filter()`][File::boolean_filter_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn boolean_filter(
&self,
key: impl Into<&'a BStr>,
key: impl Key,
filter: &mut MetadataFilter,
) -> Option<Result<bool, value::Error>> {
let key = crate::parse::key(key.into())?;
self.boolean_filter(key.section_name, key.subsection_name, key.value_name, filter)
self.boolean_filter_by(key.section_name(), key.subsection_name(), key.name(), filter)
}

/// Like [`value()`][File::value()], but returning an `Option` if the integer wasn't found.
pub fn integer(
pub fn integer_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
key: impl AsRef<str>,
) -> Option<Result<i64, value::Error>> {
self.integer_filter(section_name, subsection_name, key, &mut |_| true)
self.integer_filter_by(section_name, subsection_name, key, &mut |_| true)
}

/// Like [`integer()`][File::integer()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integer_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Result<i64, value::Error>> {
self.integer_filter_by_key(key, &mut |_| true)
/// Like [`integer()`][File::integer_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integer(&self, key: impl Key) -> Option<Result<i64, value::Error>> {
self.integer_filter(key, &mut |_| true)
}

/// Like [`integer()`][File::integer()], but the section containing the returned value must pass `filter` as well.
pub fn integer_filter(
/// Like [`integer()`][File::integer_by()], but the section containing the returned value must pass `filter` as well.
pub fn integer_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand All @@ -178,35 +175,33 @@ impl<'event> File<'event> {
}))
}

/// Like [`integer_filter()`][File::integer_filter()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integer_filter_by_key<'a>(
/// Like [`integer_filter()`][File::integer_filter_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integer_filter(
&self,
key: impl Into<&'a BStr>,
key: impl Key,
filter: &mut MetadataFilter,
) -> Option<Result<i64, value::Error>> {
let key = crate::parse::key(key.into())?;
self.integer_filter(key.section_name, key.subsection_name, key.value_name, filter)
self.integer_filter_by(key.section_name(), key.subsection_name(), key.name(), filter)
}

/// Similar to [`values(…)`][File::values()] but returning strings if at least one of them was found.
pub fn strings(
pub fn strings_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
key: impl AsRef<str>,
) -> Option<Vec<Cow<'_, BStr>>> {
self.raw_values(section_name.as_ref(), subsection_name, key.as_ref())
self.raw_values_by(section_name.as_ref(), subsection_name, key.as_ref())
.ok()
}

/// Like [`strings()`][File::strings()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn strings_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Vec<Cow<'_, BStr>>> {
let key = crate::parse::key(key.into())?;
self.strings(key.section_name, key.subsection_name, key.value_name)
/// Like [`strings()`][File::strings_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn strings(&self, key: impl Key) -> Option<Vec<Cow<'_, BStr>>> {
self.strings_by(key.section_name(), key.subsection_name(), key.name())
}

/// Similar to [`strings(…)`][File::strings()], but all values are in sections that passed `filter`.
pub fn strings_filter(
/// Similar to [`strings(…)`][File::strings_by()], but all values are in sections that passed `filter`.
pub fn strings_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand All @@ -217,35 +212,34 @@ impl<'event> File<'event> {
.ok()
}

/// Like [`strings_filter()`][File::strings_filter()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn strings_filter_by_key<'a>(
/// Like [`strings_filter()`][File::strings_filter_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn strings_filter(
&self,
key: impl Into<&'a BStr>,
key: impl Key,
filter: &mut MetadataFilter,
) -> Option<Vec<Cow<'_, BStr>>> {
let key = crate::parse::key(key.into())?;
self.strings_filter(key.section_name, key.subsection_name, key.value_name, filter)
self.strings_filter_by(key.section_name(), key.subsection_name(), key.name(), filter)
}

/// Similar to [`values(…)`][File::values()] but returning integers if at least one of them was found
/// and if none of them overflows.
pub fn integers(
pub fn integers_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
key: impl AsRef<str>,
) -> Option<Result<Vec<i64>, value::Error>> {
self.integers_filter(section_name, subsection_name, key, &mut |_| true)
self.integers_filter_by(section_name, subsection_name, key, &mut |_| true)
}

/// Like [`integers()`][File::integers()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integers_by_key<'a>(&self, key: impl Into<&'a BStr>) -> Option<Result<Vec<i64>, value::Error>> {
self.integers_filter_by_key(key, &mut |_| true)
/// Like [`integers()`][File::integers_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integers(&self, key: impl Key) -> Option<Result<Vec<i64>, value::Error>> {
self.integers_filter(key, &mut |_| true)
}

/// Similar to [`integers(…)`][File::integers()] but all integers are in sections that passed `filter`
/// Similar to [`integers(…)`][File::integers_by()] but all integers are in sections that passed `filter`
/// and that are not overflowing.
pub fn integers_filter(
pub fn integers_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand All @@ -267,13 +261,12 @@ impl<'event> File<'event> {
})
}

/// Like [`integers_filter()`][File::integers_filter()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integers_filter_by_key<'a>(
/// Like [`integers_filter()`][File::integers_filter_by()], but suitable for statically known `key`s like `remote.origin.url`.
pub fn integers_filter(
&self,
key: impl Into<&'a BStr>,
key: impl Key,
filter: &mut MetadataFilter,
) -> Option<Result<Vec<i64>, value::Error>> {
let key = crate::parse::key(key.into())?;
self.integers_filter(key.section_name, key.subsection_name, key.value_name, filter)
self.integers_filter_by(key.section_name(), key.subsection_name(), key.name(), filter)
}
}
32 changes: 28 additions & 4 deletions gix-config/src/file/access/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ use crate::{
///
/// These functions are the raw value API, returning normalized byte strings.
impl<'event> File<'event> {
/// TODO
pub fn raw_value(
&self,
key: impl Key,
) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
self.raw_value_by(key.section_name(), key.subsection_name(), key.name())
}

/// Returns an uninterpreted value given a section, an optional subsection
/// and key.
///
/// Consider [`Self::raw_values()`] if you want to get all values of
/// a multivar instead.
pub fn raw_value(
pub fn raw_value_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand Down Expand Up @@ -151,6 +159,14 @@ impl<'event> File<'event> {
Err(lookup::existing::Error::KeyMissing)
}

/// TODO
pub fn raw_values(
&self,
key: impl Key,
) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
self.raw_values_by(key.section_name(), key.subsection_name(), key.name())
}

/// Returns all uninterpreted values given a section, an optional subsection
/// ain order of occurrence.
///
Expand Down Expand Up @@ -189,7 +205,7 @@ impl<'event> File<'event> {
///
/// Consider [`Self::raw_value`] if you want to get the resolved single
/// value for a given key, if your key does not support multi-valued values.
pub fn raw_values(
pub fn raw_values_by(
&self,
section_name: impl AsRef<str>,
subsection_name: Option<&BStr>,
Expand Down Expand Up @@ -237,6 +253,14 @@ impl<'event> File<'event> {
}
}

/// TODO
pub fn raw_values_mut(
&mut self,
key: &'event dyn Key,
) -> Result<MultiValueMut<'_, '_, 'event>, lookup::existing::Error> {
self.raw_values_mut_by(key.section_name(), key.subsection_name(), key.name())
}

/// Returns mutable references to all uninterpreted values given a section,
/// an optional subsection and key.
///
Expand Down Expand Up @@ -287,7 +311,7 @@ impl<'event> File<'event> {
///
/// Note that this operation is relatively expensive, requiring a full
/// traversal of the config.
pub fn raw_values_mut<'lookup>(
pub fn raw_values_mut_by<'lookup>(
&mut self,
section_name: impl AsRef<str>,
subsection_name: Option<&'lookup BStr>,
Expand Down Expand Up @@ -568,7 +592,7 @@ impl<'event> File<'event> {
Iter: IntoIterator<Item = Item>,
Item: Into<&'a BStr>,
{
self.raw_values_mut(section_name, subsection_name, key.as_ref())
self.raw_values_mut_by(section_name, subsection_name, key.as_ref())
.map(|mut v| v.set_values(new_values))
}
}