From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- vendor/gix/src/config/tree/keys.rs | 629 +++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 vendor/gix/src/config/tree/keys.rs (limited to 'vendor/gix/src/config/tree/keys.rs') diff --git a/vendor/gix/src/config/tree/keys.rs b/vendor/gix/src/config/tree/keys.rs new file mode 100644 index 000000000..1cdd187d0 --- /dev/null +++ b/vendor/gix/src/config/tree/keys.rs @@ -0,0 +1,629 @@ +#![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}, +}; + +/// Implements a value without any constraints, i.e. a any value. +pub struct Any { + /// The key of the value in the git configuration. + pub name: &'static str, + /// The parent section of the key. + pub section: &'static dyn Section, + /// The subsection requirement to use. + pub subsection_requirement: Option, + /// A link to other resources that might be eligible as value. + pub link: Option, + /// A note about this key. + pub note: Option, + /// The way validation and transformation should happen. + validate: T, +} + +/// Init +impl Any { + /// Create a new instance from `name` and `section` + pub const fn new(name: &'static str, section: &'static dyn Section) -> Self { + Any::new_with_validate(name, section, validate::All) + } +} + +/// Init other validate implementations +impl Any { + /// Create a new instance from `name` and `section` + 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, + } + } +} + +/// Builder +impl Any { + /// Set the subsection requirement to non-default values. + pub const fn with_subsection_requirement(mut self, requirement: Option) -> Self { + self.subsection_requirement = requirement; + self + } + + /// Associate an environment variable with this key. + /// + /// This is mainly useful for enriching error messages. + pub const fn with_environment_override(mut self, var: &'static str) -> Self { + self.link = Some(Link::EnvironmentOverride(var)); + self + } + + /// Set a link to another key which serves as fallback to provide a value if this key is not set. + pub const fn with_fallback(mut self, key: &'static dyn Key) -> Self { + self.link = Some(Link::FallbackKey(key)); + self + } + + /// Attach an informative message to this key. + pub const fn with_note(mut self, message: &'static str) -> Self { + self.note = Some(Note::Informative(message)); + self + } + + /// Inform about a deviation in how this key is interpreted. + pub const fn with_deviation(mut self, message: &'static str) -> Self { + self.note = Some(Note::Deviation(message)); + self + } +} + +/// Conversion +impl Any { + /// Try to convert `value` into a refspec suitable for the `op` operation. + pub fn try_into_refspec( + &'static self, + value: std::borrow::Cow<'_, BStr>, + op: gix_refspec::parse::Operation, + ) -> Result { + 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)) + } + + /// Try to interpret `value` as UTF-8 encoded string. + pub fn try_into_string(&'static self, value: Cow<'_, BStr>) -> Result { + 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 Debug for Any { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.logical_name().fmt(f) + } +} + +impl std::fmt::Display for Any { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.logical_name()) + } +} + +impl Key for Any { + 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() + } +} + +/// A key which represents a date. +pub type Time = Any; + +/// The `core.(filesRefLockTimeout|packedRefsTimeout)` keys, or any other lock timeout for that matter. +pub type LockTimeout = Any; + +/// Keys specifying durations in milliseconds. +pub type DurationInMilliseconds = Any; + +/// A key which represents any unsigned integer. +pub type UnsignedInteger = Any; + +/// A key that represents a remote name, either as url or symbolic name. +pub type RemoteName = Any; + +/// A key that represents a boolean value. +pub type Boolean = Any; + +/// A key that represents an executable program, shell script or shell commands. +pub type Program = Any; + +/// A key that represents an executable program as identified by name or path. +pub type Executable = Any; + +/// A key that represents a path (to a resource). +pub type Path = Any; + +/// A key that represents a URL. +pub type Url = Any; + +/// A key that represents a UTF-8 string. +pub type String = Any; + +/// A key that represents a RefSpec for pushing. +pub type PushRefSpec = Any; + +/// A key that represents a RefSpec for fetching. +pub type FetchRefSpec = Any; + +mod duration { + use std::time::Duration; + + use crate::{ + config, + config::tree::{keys::DurationInMilliseconds, Section}, + }; + + impl DurationInMilliseconds { + /// Create a new instance. + pub const fn new_duration(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, super::validate::DurationInMilliseconds) + } + + /// Return a valid duration as parsed from an integer that is interpreted as milliseconds. + pub fn try_into_duration( + &'static self, + value: Result, + ) -> Result { + 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 { + /// Create a new instance. + pub const fn new_lock_timeout(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, super::validate::LockTimeout) + } + + /// Return information on how long to wait for locked files. + pub fn try_into_lock_timeout( + &'static self, + value: Result, + ) -> Result { + 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 { + /// Create a new instance. + pub const fn new_push_refspec(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::PushRefSpec) + } + } + + impl FetchRefSpec { + /// Create a new instance. + 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 { + /// Create a new instance. + pub const fn new_url(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::Url) + } + + /// Try to parse `value` as URL. + pub fn try_into_url(&'static self, value: Cow<'_, BStr>) -> Result { + gix_url::parse(value.as_ref()) + .map_err(|err| config::url::Error::from_value(self, value.into_owned()).with_source(err)) + } + } +} + +impl String { + /// Create a new instance. + pub const fn new_string(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::String) + } +} + +impl Program { + /// Create a new instance. + pub const fn new_program(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::Program) + } +} + +impl Executable { + /// Create a new instance. + pub const fn new_executable(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::Executable) + } +} + +impl Path { + /// Create a new instance. + 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 { + /// Create a new instance. + pub const fn new_unsigned_integer(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, super::validate::UnsignedInteger) + } + + /// Convert `value` into a `usize` or wrap it into a specialized error. + pub fn try_into_usize( + &'static self, + value: Result, + ) -> Result { + 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)) + }) + } + + /// Convert `value` into a `u64` or wrap it into a specialized error. + pub fn try_into_u64( + &'static self, + value: Result, + ) -> Result { + 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)) + }) + } + + /// Convert `value` into a `u32` or wrap it into a specialized error. + pub fn try_into_u32( + &'static self, + value: Result, + ) -> Result { + 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 { + /// Create a new instance. + pub const fn new_time(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::Time) + } + + /// Convert the `value` into a date if possible, with `now` as reference time for relative dates. + pub fn try_into_time( + &self, + value: Cow<'_, BStr>, + now: Option, + ) -> Result { + 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 { + /// Create a new instance. + pub const fn new_boolean(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, validate::Boolean) + } + + /// Process the `value` into a result with an improved error message. + /// + /// `value` is expected to be provided by [`gix_config::File::boolean()`]. + pub fn enrich_error( + &'static self, + value: Result, + ) -> Result { + 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 { + /// Create a new instance. + pub const fn new_remote_name(name: &'static str, section: &'static dyn Section) -> Self { + Self::new_with_validate(name, section, super::validate::RemoteName) + } + + /// Try to validate `name` as symbolic remote name and return it. + #[allow(clippy::result_large_err)] + pub fn try_into_symbolic_name( + &'static self, + name: Cow<'_, BStr>, + ) -> Result { + crate::remote::name::validated(name.into_owned()) + .map_err(|err| config::remote::symbolic_name::Error::from(self).with_source(err)) + } + } +} + +/// Provide a way to validate a value, or decode a value from `gix-config`. +pub trait Validate { + /// Validate `value` or return an error. + fn validate(&self, value: &BStr) -> Result<(), Box>; +} + +/// various implementations of the `Validate` trait. +pub mod validate { + use std::{borrow::Cow, error::Error}; + + use crate::{ + bstr::{BStr, ByteSlice}, + config::tree::keys::Validate, + remote, + }; + + /// Everything is valid. + #[derive(Default)] + pub struct All; + + impl Validate for All { + fn validate(&self, _value: &BStr) -> Result<(), Box> { + Ok(()) + } + } + + /// Assure that values that parse as git dates are valid. + #[derive(Default)] + pub struct Time; + + impl Validate for Time { + fn validate(&self, value: &BStr) -> Result<(), Box> { + gix_date::parse(value.to_str()?, std::time::SystemTime::now().into())?; + Ok(()) + } + } + + /// Assure that values that parse as unsigned integers are valid. + #[derive(Default)] + pub struct UnsignedInteger; + + impl Validate for UnsignedInteger { + fn validate(&self, value: &BStr) -> Result<(), Box> { + usize::try_from( + gix_config::Integer::try_from(value)? + .to_decimal() + .ok_or_else(|| format!("integer {value} cannot be represented as `usize`"))?, + )?; + Ok(()) + } + } + + /// Assure that values that parse as git booleans are valid. + #[derive(Default)] + pub struct Boolean; + + impl Validate for Boolean { + fn validate(&self, value: &BStr) -> Result<(), Box> { + gix_config::Boolean::try_from(value)?; + Ok(()) + } + } + + /// Values that are git remotes, symbolic or urls + #[derive(Default)] + pub struct RemoteName; + impl Validate for RemoteName { + fn validate(&self, value: &BStr) -> Result<(), Box> { + remote::Name::try_from(Cow::Borrowed(value)) + .map_err(|_| format!("Illformed UTF-8 in remote name: \"{}\"", value.to_str_lossy()))?; + Ok(()) + } + } + + /// Values that are programs - everything is allowed. + #[derive(Default)] + pub struct Program; + impl Validate for Program { + fn validate(&self, _value: &BStr) -> Result<(), Box> { + Ok(()) + } + } + + /// Values that are programs executables, everything is allowed. + #[derive(Default)] + pub struct Executable; + impl Validate for Executable { + fn validate(&self, _value: &BStr) -> Result<(), Box> { + Ok(()) + } + } + + /// Values that parse as URLs. + #[derive(Default)] + pub struct Url; + impl Validate for Url { + fn validate(&self, value: &BStr) -> Result<(), Box> { + gix_url::parse(value)?; + Ok(()) + } + } + + /// Values that parse as ref-specs for pushing. + #[derive(Default)] + pub struct PushRefSpec; + impl Validate for PushRefSpec { + fn validate(&self, value: &BStr) -> Result<(), Box> { + gix_refspec::parse(value, gix_refspec::parse::Operation::Push)?; + Ok(()) + } + } + + /// Values that parse as ref-specs for pushing. + #[derive(Default)] + pub struct FetchRefSpec; + impl Validate for FetchRefSpec { + fn validate(&self, value: &BStr) -> Result<(), Box> { + gix_refspec::parse(value, gix_refspec::parse::Operation::Fetch)?; + Ok(()) + } + } + + /// Timeouts used for file locks. + pub struct LockTimeout; + impl Validate for LockTimeout { + fn validate(&self, value: &BStr) -> Result<(), Box> { + 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(()) + } + } + + /// Durations in milliseconds. + pub struct DurationInMilliseconds; + impl Validate for DurationInMilliseconds { + fn validate(&self, value: &BStr) -> Result<(), Box> { + 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(()) + } + } + + /// A UTF-8 string. + pub struct String; + impl Validate for String { + fn validate(&self, value: &BStr) -> Result<(), Box> { + value.to_str()?; + Ok(()) + } + } + + /// Any path - everything is allowed. + pub struct Path; + impl Validate for Path { + fn validate(&self, _value: &BStr) -> Result<(), Box> { + Ok(()) + } + } +} -- cgit v1.2.3