use std::fmt::Debug;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
use super::DependencySpec;
use super::ExecutionError;
use super::ExecutionErrorKind;
use super::ValidationPath;
use super::ValidationValue;
pub struct BoundValidationContext<'a> {
values: &'a [ValidationValue<'a>],
paths: Option<&'a [ValidationPath]>,
}
impl<'a> BoundValidationContext<'a> {
#[must_use]
#[inline]
pub fn new(values: &'a [ValidationValue<'a>]) -> Self {
Self { values, paths: None }
}
pub fn new_with_paths(
values: &'a [ValidationValue<'a>],
paths: &'a [ValidationPath],
) -> Result<Self, ExecutionError> {
if values.len() != paths.len() {
return Err(ExecutionError::new(ExecutionErrorKind::AdapterContractViolation));
}
Ok(Self {
values,
paths: Some(paths),
})
}
#[inline]
pub fn value(&self, index: usize) -> Result<ValidationValue<'a>, ExecutionError> {
self.values
.get(index)
.copied()
.ok_or_else(|| ExecutionError::new(ExecutionErrorKind::AdapterContractViolation))
}
#[inline]
pub fn dependency_path(&self, index: usize) -> Result<&ValidationPath, ExecutionError> {
match self.paths {
Some(paths) => paths
.get(index)
.ok_or_else(|| ExecutionError::new(ExecutionErrorKind::AdapterContractViolation)),
None if index < self.values.len() => Ok(&ROOT_PATH),
None => Err(ExecutionError::new(ExecutionErrorKind::AdapterContractViolation)),
}
}
#[inline]
pub fn typed<T: 'static>(&self, index: usize) -> Result<&'a T, ExecutionError> {
match self.value(index)? {
ValidationValue::Typed(value) => value
.downcast_ref::<T>()
.ok_or_else(|| ExecutionError::new(ExecutionErrorKind::DependencyTypeMismatch)),
ValidationValue::Missing => Err(ExecutionError::new(ExecutionErrorKind::MissingRequiredDependencyValue)),
ValidationValue::Text(_) => Err(ExecutionError::new(ExecutionErrorKind::DependencyTypeMismatch)),
}
}
#[inline]
pub fn optional_typed<T: 'static>(&self, index: usize) -> Result<Option<&'a T>, ExecutionError> {
match self.value(index)? {
ValidationValue::Missing => Ok(None),
ValidationValue::Typed(value) => value
.downcast_ref::<T>()
.map(Some)
.ok_or_else(|| ExecutionError::new(ExecutionErrorKind::DependencyTypeMismatch)),
ValidationValue::Text(_) => Err(ExecutionError::new(ExecutionErrorKind::DependencyTypeMismatch)),
}
}
#[inline]
pub fn text(&self, index: usize) -> Result<&'a str, ExecutionError> {
match self.value(index)? {
ValidationValue::Text(value) => Ok(value),
ValidationValue::Missing => Err(ExecutionError::new(ExecutionErrorKind::MissingRequiredDependencyValue)),
ValidationValue::Typed(_) => Err(ExecutionError::new(ExecutionErrorKind::DependencyTypeMismatch)),
}
}
pub(crate) fn check_specs(&self, specs: &[DependencySpec]) -> Result<(), ExecutionError> {
if self.values.len() != specs.len() || self.paths.is_some_and(|paths| paths.len() != specs.len()) {
return Err(ExecutionError::new(ExecutionErrorKind::AdapterContractViolation));
}
for (index, spec) in specs.iter().copied().enumerate() {
let value = self.value(index)?;
let error_kind = if value.is_missing() {
if !spec.optional() {
ExecutionErrorKind::MissingRequiredDependencyValue
} else {
continue;
}
} else if !spec.input().accepts(value) {
ExecutionErrorKind::DependencyTypeMismatch
} else {
continue;
};
let path = self.dependency_path(index)?.clone();
return Err(ExecutionError::new(error_kind)
.with_dependency(spec.name())
.with_path(path));
}
Ok(())
}
}
static ROOT_PATH: ValidationPath = ValidationPath::root();
impl Debug for BoundValidationContext<'_> {
fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
formatter
.debug_struct("BoundValidationContext")
.field("slot_count", &self.values.len())
.finish()
}
}