#![allow(clippy::result_large_err)]
use std::{
borrow::Cow,
error::Error,
fmt::{Debug, Formatter},
};
use crate::{
bstr::BStr,
config,
config::tree::{Key, Link, Note, Section, SubSectionRequirement},
};
pub struct Any<T: Validate = validate::All> {
pub name: &'static str,
pub section: &'static dyn Section,
pub subsection_requirement: Option<SubSectionRequirement>,
pub link: Option<Link>,
pub note: Option<Note>,
validate: T,
}
impl Any<validate::All> {
pub const fn new(name: &'static str, section: &'static dyn Section) -> Self {
Any::new_with_validate(name, section, validate::All)
}
}
impl<T: Validate> Any<T> {
pub const fn new_with_validate(name: &'static str, section: &'static dyn Section, validate: T) -> Self {
Any {
name,
section,
subsection_requirement: Some(SubSectionRequirement::Never),
link: None,
note: None,
validate,
}
}
}
impl<T: Validate> Any<T> {
pub const fn with_subsection_requirement(mut self, requirement: Option<SubSectionRequirement>) -> Self {
self.subsection_requirement = requirement;
self
}
pub const fn with_environment_override(mut self, var: &'static str) -> Self {
self.link = Some(Link::EnvironmentOverride(var));
self
}
pub const fn with_fallback(mut self, key: &'static dyn Key) -> Self {
self.link = Some(Link::FallbackKey(key));
self
}
pub const fn with_note(mut self, message: &'static str) -> Self {
self.note = Some(Note::Informative(message));
self
}
pub const fn with_deviation(mut self, message: &'static str) -> Self {
self.note = Some(Note::Deviation(message));
self
}
}
impl<T: Validate> Any<T> {
pub fn try_into_refspec(
&'static self,
value: std::borrow::Cow<'_, BStr>,
op: gix_refspec::parse::Operation,
) -> Result<gix_refspec::RefSpec, config::refspec::Error> {
gix_refspec::parse(value.as_ref(), op)
.map(|spec| spec.to_owned())
.map_err(|err| config::refspec::Error::from_value(self, value.into_owned()).with_source(err))
}
pub fn try_into_string(&'static self, value: Cow<'_, BStr>) -> Result<std::string::String, config::string::Error> {
use crate::bstr::ByteVec;
Vec::from(value.into_owned()).into_string().map_err(|err| {
let utf8_err = err.utf8_error().clone();
config::string::Error::from_value(self, err.into_vec().into()).with_source(utf8_err)
})
}
}
impl<T: Validate> Debug for Any<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.logical_name().fmt(f)
}
}
impl<T: Validate> std::fmt::Display for Any<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.logical_name())
}
}
impl<T: Validate> Key for Any<T> {
fn name(&self) -> &str {
self.name
}
fn validate(&self, value: &BStr) -> Result<(), config::tree::key::validate::Error> {
Ok(self.validate.validate(value)?)
}
fn section(&self) -> &dyn Section {
self.section
}
fn subsection_requirement(&self) -> Option<&SubSectionRequirement> {
self.subsection_requirement.as_ref()
}
fn link(&self) -> Option<&Link> {
self.link.as_ref()
}
fn note(&self) -> Option<&Note> {
self.note.as_ref()
}
}
pub type Time = Any<validate::Time>;
pub type LockTimeout = Any<validate::LockTimeout>;
pub type DurationInMilliseconds = Any<validate::DurationInMilliseconds>;
pub type UnsignedInteger = Any<validate::UnsignedInteger>;
pub type RemoteName = Any<validate::RemoteName>;
pub type Boolean = Any<validate::Boolean>;
pub type Program = Any<validate::Program>;
pub type Executable = Any<validate::Executable>;
pub type Path = Any<validate::Path>;
pub type Url = Any<validate::Url>;
pub type String = Any<validate::String>;
pub type PushRefSpec = Any<validate::PushRefSpec>;
pub type FetchRefSpec = Any<validate::FetchRefSpec>;
mod duration {
use std::time::Duration;
use crate::{
config,
config::tree::{keys::DurationInMilliseconds, Section},
};
impl DurationInMilliseconds {
pub const fn new_duration(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, super::validate::DurationInMilliseconds)
}
pub fn try_into_duration(
&'static self,
value: Result<i64, gix_config::value::Error>,
) -> Result<std::time::Duration, config::duration::Error> {
let value = value.map_err(|err| config::duration::Error::from(self).with_source(err))?;
Ok(match value {
val if val < 0 => Duration::from_secs(u64::MAX),
val => Duration::from_millis(val.try_into().expect("i64 to u64 always works if positive")),
})
}
}
}
mod lock_timeout {
use std::time::Duration;
use gix_lock::acquire::Fail;
use crate::{
config,
config::tree::{keys::LockTimeout, Section},
};
impl LockTimeout {
pub const fn new_lock_timeout(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, super::validate::LockTimeout)
}
pub fn try_into_lock_timeout(
&'static self,
value: Result<i64, gix_config::value::Error>,
) -> Result<gix_lock::acquire::Fail, config::lock_timeout::Error> {
let value = value.map_err(|err| config::lock_timeout::Error::from(self).with_source(err))?;
Ok(match value {
val if val < 0 => Fail::AfterDurationWithBackoff(Duration::from_secs(u64::MAX)),
val if val == 0 => Fail::Immediately,
val => Fail::AfterDurationWithBackoff(Duration::from_millis(
val.try_into().expect("i64 to u64 always works if positive"),
)),
})
}
}
}
mod refspecs {
use crate::config::tree::{
keys::{validate, FetchRefSpec, PushRefSpec},
Section,
};
impl PushRefSpec {
pub const fn new_push_refspec(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::PushRefSpec)
}
}
impl FetchRefSpec {
pub const fn new_fetch_refspec(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::FetchRefSpec)
}
}
}
mod url {
use std::borrow::Cow;
use crate::{
bstr::BStr,
config,
config::tree::{
keys::{validate, Url},
Section,
},
};
impl Url {
pub const fn new_url(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::Url)
}
pub fn try_into_url(&'static self, value: Cow<'_, BStr>) -> Result<gix_url::Url, config::url::Error> {
gix_url::parse(value.as_ref())
.map_err(|err| config::url::Error::from_value(self, value.into_owned()).with_source(err))
}
}
}
impl String {
pub const fn new_string(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::String)
}
}
impl Program {
pub const fn new_program(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::Program)
}
}
impl Executable {
pub const fn new_executable(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::Executable)
}
}
impl Path {
pub const fn new_path(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::Path)
}
}
mod workers {
use crate::config::tree::{keys::UnsignedInteger, Section};
impl UnsignedInteger {
pub const fn new_unsigned_integer(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, super::validate::UnsignedInteger)
}
pub fn try_into_usize(
&'static self,
value: Result<i64, gix_config::value::Error>,
) -> Result<usize, crate::config::unsigned_integer::Error> {
value
.map_err(|err| crate::config::unsigned_integer::Error::from(self).with_source(err))
.and_then(|value| {
value
.try_into()
.map_err(|_| crate::config::unsigned_integer::Error::from(self))
})
}
pub fn try_into_u64(
&'static self,
value: Result<i64, gix_config::value::Error>,
) -> Result<u64, crate::config::unsigned_integer::Error> {
value
.map_err(|err| crate::config::unsigned_integer::Error::from(self).with_source(err))
.and_then(|value| {
value
.try_into()
.map_err(|_| crate::config::unsigned_integer::Error::from(self))
})
}
pub fn try_into_u32(
&'static self,
value: Result<i64, gix_config::value::Error>,
) -> Result<u32, crate::config::unsigned_integer::Error> {
value
.map_err(|err| crate::config::unsigned_integer::Error::from(self).with_source(err))
.and_then(|value| {
value
.try_into()
.map_err(|_| crate::config::unsigned_integer::Error::from(self))
})
}
}
}
mod time {
use std::borrow::Cow;
use crate::{
bstr::{BStr, ByteSlice},
config::tree::{
keys::{validate, Time},
Section,
},
};
impl Time {
pub const fn new_time(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::Time)
}
pub fn try_into_time(
&self,
value: Cow<'_, BStr>,
now: Option<std::time::SystemTime>,
) -> Result<gix_date::Time, gix_date::parse::Error> {
gix_date::parse(
value
.as_ref()
.to_str()
.map_err(|_| gix_date::parse::Error::InvalidDateString {
input: value.to_string(),
})?,
now,
)
}
}
}
mod boolean {
use crate::{
config,
config::tree::{
keys::{validate, Boolean},
Section,
},
};
impl Boolean {
pub const fn new_boolean(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, validate::Boolean)
}
pub fn enrich_error(
&'static self,
value: Result<bool, gix_config::value::Error>,
) -> Result<bool, config::boolean::Error> {
value.map_err(|err| config::boolean::Error::from(self).with_source(err))
}
}
}
mod remote_name {
use std::borrow::Cow;
use crate::{
bstr::{BStr, BString},
config,
config::tree::{keys::RemoteName, Section},
};
impl RemoteName {
pub const fn new_remote_name(name: &'static str, section: &'static dyn Section) -> Self {
Self::new_with_validate(name, section, super::validate::RemoteName)
}
#[allow(clippy::result_large_err)]
pub fn try_into_symbolic_name(
&'static self,
name: Cow<'_, BStr>,
) -> Result<BString, config::remote::symbolic_name::Error> {
crate::remote::name::validated(name.into_owned())
.map_err(|err| config::remote::symbolic_name::Error::from(self).with_source(err))
}
}
}
pub trait Validate {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;
}
pub mod validate {
use std::{borrow::Cow, error::Error};
use crate::{
bstr::{BStr, ByteSlice},
config::tree::keys::Validate,
remote,
};
#[derive(Default)]
pub struct All;
impl Validate for All {
fn validate(&self, _value: &BStr) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
Ok(())
}
}
#[derive(Default)]
pub struct Time;
impl Validate for Time {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
gix_date::parse(value.to_str()?, std::time::SystemTime::now().into())?;
Ok(())
}
}
#[derive(Default)]
pub struct UnsignedInteger;
impl Validate for UnsignedInteger {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
usize::try_from(
gix_config::Integer::try_from(value)?
.to_decimal()
.ok_or_else(|| format!("integer {value} cannot be represented as `usize`"))?,
)
.map_err(|_| "cannot use sign for unsigned integer")?;
Ok(())
}
}
#[derive(Default)]
pub struct Boolean;
impl Validate for Boolean {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
gix_config::Boolean::try_from(value)?;
Ok(())
}
}
#[derive(Default)]
pub struct RemoteName;
impl Validate for RemoteName {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
remote::Name::try_from(Cow::Borrowed(value))
.map_err(|_| format!("Illformed UTF-8 in remote name: \"{}\"", value.to_str_lossy()))?;
Ok(())
}
}
#[derive(Default)]
pub struct Program;
impl Validate for Program {
fn validate(&self, _value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
Ok(())
}
}
#[derive(Default)]
pub struct Executable;
impl Validate for Executable {
fn validate(&self, _value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
Ok(())
}
}
#[derive(Default)]
pub struct Url;
impl Validate for Url {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
gix_url::parse(value)?;
Ok(())
}
}
#[derive(Default)]
pub struct PushRefSpec;
impl Validate for PushRefSpec {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
gix_refspec::parse(value, gix_refspec::parse::Operation::Push)?;
Ok(())
}
}
#[derive(Default)]
pub struct FetchRefSpec;
impl Validate for FetchRefSpec {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
gix_refspec::parse(value, gix_refspec::parse::Operation::Fetch)?;
Ok(())
}
}
pub struct LockTimeout;
impl Validate for LockTimeout {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
let value = gix_config::Integer::try_from(value)?
.to_decimal()
.ok_or_else(|| format!("integer {value} cannot be represented as integer"));
super::super::Core::FILES_REF_LOCK_TIMEOUT.try_into_lock_timeout(Ok(value?))?;
Ok(())
}
}
pub struct DurationInMilliseconds;
impl Validate for DurationInMilliseconds {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
let value = gix_config::Integer::try_from(value)?
.to_decimal()
.ok_or_else(|| format!("integer {value} cannot be represented as integer"));
super::super::gitoxide::Http::CONNECT_TIMEOUT.try_into_duration(Ok(value?))?;
Ok(())
}
}
pub struct String;
impl Validate for String {
fn validate(&self, value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
value.to_str()?;
Ok(())
}
}
pub struct Path;
impl Validate for Path {
fn validate(&self, _value: &BStr) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
Ok(())
}
}
}