use bstr::{BStr, BString};
use gix_error::ExnMessageResult;
use gix_error::{ErrorExt, validation};
use crate::{AsBStrOpt, AsKey, File, file::Metadata};
impl File {
pub fn string(&self, key: impl AsKey) -> Option<BString> {
self.string_filter(key, |_| true)
}
pub fn string_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
) -> Option<BString> {
self.string_filter_by(section_name, subsection_name, value_name, |_| true)
}
pub fn string_filter(&self, key: impl AsKey, filter: impl FnMut(&Metadata) -> bool) -> Option<BString> {
let key = key.try_as_key()?;
self.raw_value_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
.ok()
}
pub fn string_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
filter: impl FnMut(&Metadata) -> bool,
) -> Option<BString> {
self.raw_value_filter_by(section_name, subsection_name, value_name, filter)
.ok()
}
pub fn path(&self, key: impl AsKey) -> Option<crate::Path> {
self.path_filter(key, |_| true)
}
pub fn path_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
) -> Option<crate::Path> {
self.path_filter_by(section_name, subsection_name, value_name, |_| true)
}
pub fn path_filter(&self, key: impl AsKey, filter: impl FnMut(&Metadata) -> bool) -> Option<crate::Path> {
let key = key.try_as_key()?;
self.path_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
}
pub fn path_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
filter: impl FnMut(&Metadata) -> bool,
) -> Option<crate::Path> {
self.raw_value_filter_by(section_name, subsection_name, value_name, filter)
.ok()
.map(crate::Path::from)
}
pub fn boolean(&self, key: impl AsKey) -> ExnMessageResult<Option<bool>> {
self.boolean_filter(key, |_| true)
}
pub fn boolean_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
) -> ExnMessageResult<Option<bool>> {
self.boolean_filter_by(section_name, subsection_name, value_name, |_| true)
}
pub fn boolean_filter(
&self,
key: impl AsKey,
filter: impl FnMut(&Metadata) -> bool,
) -> ExnMessageResult<Option<bool>> {
let Some(key) = key.try_as_key() else {
return Ok(None);
};
self.boolean_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
}
pub fn boolean_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
mut filter: impl FnMut(&Metadata) -> bool,
) -> ExnMessageResult<Option<bool>> {
let section_name = section_name.as_ref();
let section_ids = self
.section_ids_by_name_and_subname(section_name, subsection_name.as_bstr_opt())
.ok();
let Some(section_ids) = section_ids else {
return Ok(None);
};
let key = value_name.as_ref();
for section_id in section_ids.rev() {
let section = self.sections.get(§ion_id).expect("known section id");
if !filter(section.meta()) {
continue;
}
match section.body.value_implicit_in(&self.backing, key) {
Some(Some(v)) => return crate::Boolean::try_from(v).map(|value| Some(value.into())),
Some(None) => return Ok(Some(true)),
None => continue,
}
}
Ok(None)
}
pub fn integer(&self, key: impl AsKey) -> ExnMessageResult<Option<i64>> {
self.integer_filter(key, |_| true)
}
pub fn integer_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
) -> ExnMessageResult<Option<i64>> {
self.integer_filter_by(section_name, subsection_name, value_name, |_| true)
}
pub fn integer_filter(
&self,
key: impl AsKey,
filter: impl FnMut(&Metadata) -> bool,
) -> ExnMessageResult<Option<i64>> {
let Some(key) = key.try_as_key() else {
return Ok(None);
};
self.integer_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
}
pub fn integer_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
filter: impl FnMut(&Metadata) -> bool,
) -> ExnMessageResult<Option<i64>> {
let Some(int) = self
.raw_value_filter_by(section_name, subsection_name, value_name, filter)
.ok()
else {
return Ok(None);
};
crate::Integer::try_from(BStr::new(&int))
.and_then(|b| {
b.to_decimal()
.ok_or_else(|| validation("Integer overflow").with("input", BStr::new(&int)).raise())
})
.map(Some)
}
pub fn strings(&self, key: impl AsKey) -> Option<Vec<BString>> {
let key = key.try_as_key()?;
self.strings_by(key.section_name, key.subsection_name, key.value_name)
}
pub fn strings_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
) -> Option<Vec<BString>> {
self.raw_values_by(section_name, subsection_name, value_name).ok()
}
pub fn strings_filter(&self, key: impl AsKey, filter: impl FnMut(&Metadata) -> bool) -> Option<Vec<BString>> {
let key = key.try_as_key()?;
self.strings_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
}
pub fn strings_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
filter: impl FnMut(&Metadata) -> bool,
) -> Option<Vec<BString>> {
self.raw_values_filter_by(section_name, subsection_name, value_name, filter)
.ok()
}
pub fn integers(&self, key: impl AsKey) -> ExnMessageResult<Option<Vec<i64>>> {
self.integers_filter(key, |_| true)
}
pub fn integers_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
) -> ExnMessageResult<Option<Vec<i64>>> {
self.integers_filter_by(section_name, subsection_name, value_name, |_| true)
}
pub fn integers_filter(
&self,
key: impl AsKey,
filter: impl FnMut(&Metadata) -> bool,
) -> ExnMessageResult<Option<Vec<i64>>> {
let Some(key) = key.try_as_key() else {
return Ok(None);
};
self.integers_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
}
pub fn integers_filter_by(
&self,
section_name: impl AsRef<str>,
subsection_name: impl AsBStrOpt,
value_name: impl AsRef<str>,
filter: impl FnMut(&Metadata) -> bool,
) -> ExnMessageResult<Option<Vec<i64>>> {
let Some(values) = self
.raw_values_filter_by(section_name, subsection_name, value_name, filter)
.ok()
else {
return Ok(None);
};
values
.into_iter()
.map(|v| {
crate::Integer::try_from(BStr::new(&v)).and_then(|int| {
int.to_decimal()
.ok_or_else(|| validation("Integer overflow").with("input", BStr::new(&v)).raise())
})
})
.collect::<Result<Vec<_>, _>>()
.map(Some)
}
}