Skip to main content

BoundValidator

Struct BoundValidator 

Source
pub struct BoundValidator { /* private fields */ }
Expand description

A bound occurrence that owns a reusable prepared validator instance.

A prepared validator reports only whether its execution was valid or invalid. Callers decide whether an occurrence should be skipped and build the corresponding ValidationOutcome themselves.

§Examples

use std::sync::Arc;

use qubit_validator::ArgumentReader;
use qubit_validator::BindError;
use qubit_validator::BoundValidationContext;
use qubit_validator::ExecutionError;
use qubit_validator::InputType;
use qubit_validator::PreparedOutcome;
use qubit_validator::PreparedValidator;
use qubit_validator::ValidationOutcome;
use qubit_validator::ValidationValue;
use qubit_validator::ValidatorDescriptor;
use qubit_validator::ValidatorId;
use qubit_validator::ValidatorSignature;

struct AcceptAll;

impl PreparedValidator for AcceptAll {
    fn input_type(&self) -> InputType { InputType::Text }
    fn dependency_specs(&self) -> &'static [qubit_validator::DependencySpec] { &[] }
    fn validate(
        &self,
        _: ValidationValue<'_>,
        _: &BoundValidationContext<'_>,
    ) -> Result<PreparedOutcome, ExecutionError> {
        Ok(PreparedOutcome::Valid)
    }
}

fn prepare(
    _reader: &mut ArgumentReader<'_>,
) -> Result<Arc<dyn PreparedValidator>, BindError> {
    Ok(Arc::new(AcceptAll))
}

static SIGNATURES: &[ValidatorSignature] = &[
    ValidatorSignature::new(InputType::Text, &[], prepare),
];
let descriptor = ValidatorDescriptor::new(SIGNATURES);
let bound = descriptor.bind(ValidatorId::new("example.accept_all"), 0, &[])?;
let outcome = bound.validate(
    ValidationValue::Text("accepted"),
    &BoundValidationContext::new(&[]),
)?;
assert_eq!(outcome, ValidationOutcome::Valid);

Implementations§

Source§

impl BoundValidator

Source

pub fn try_from_prepared<T: 'static>( rule_id: ValidatorId, prepared: Arc<dyn PreparedValidator>, ) -> Result<Self, BindError>

Creates a zero-dependency binding for an already prepared typed rule.

The binding checks that calls supply exactly T and no dependency slots before invoking the prepared validator. It does not perform registry lookup or parameter decoding.

§Type Parameters
  • T: Exact concrete value type accepted by the prepared validator.
§Parameters
  • rule_id: Stable identity attached to violations and execution errors.
  • prepared: Immutable prepared implementation shared by clones.
§Returns

A reusable bound validator accepting values of type T without dependencies.

§Errors

Returns PreparedSignatureMismatch with rule_id attached when the prepared validator does not accept exactly T or declares dependencies.

Source

pub fn validate( &self, value: ValidationValue<'_>, context: &BoundValidationContext<'_>, ) -> Result<ValidationOutcome, ExecutionError>

Validates one value after checking its erased input and dependencies.

Dependency values and paths must already follow Self::dependency_specs order. Use Self::validate_named for directly assembled dependency sets or whenever same-typed dependencies could be supplied in the wrong order.

§Parameters
  • value: Borrowed target value.
  • context: Dependency values and paths in signature order.
§Returns

The bound validation outcome.

§Errors

Returns a shape, dependency, adapter, or rule execution error.

Examples found in repository?
examples/local_registry.rs (line 162)
150fn main() -> Result<(), Box<dyn Error>> {
151    let registration = ValidatorRegistration::new(
152        ValidatorId::new("text.non_blank"),
153        &DESCRIPTOR,
154        RegistrationSource::new(env!("CARGO_PKG_NAME"), module_path!(), file!(), line!()),
155    );
156    let registry = ValidatorRegistry::from_registrations([registration])?;
157
158    assert!(registry.get("text.non_blank").is_some());
159    let validator = registry.bind("text.non_blank", InputType::Text, &[])?;
160    let context = BoundValidationContext::new(&[]);
161
162    let valid = validator.validate(ValidationValue::Text("Ada"), &context)?;
163    assert_eq!(valid, ValidationOutcome::Valid);
164
165    let invalid = validator.validate(ValidationValue::Text("   "), &context)?;
166    let ValidationOutcome::Invalid(violations) = invalid else {
167        panic!("blank text must be invalid");
168    };
169    assert_eq!(violations.len(), 1);
170    assert_eq!(violations[0].code(), ViolationCode::new("text.blank"));
171
172    assert_dependency_specs_come_from_signature()?;
173    assert_parameters_are_consumed_once()?;
174
175    #[cfg(feature = "inventory")]
176    {
177        let global = ValidatorRegistry::try_global()?;
178        assert!(global.get("text.non_blank.global").is_some());
179    }
180
181    Ok(())
182}
More examples
Hide additional examples
examples/validate_named_bench.rs (line 139)
127fn run_case(
128    label: &str,
129    bound: &BoundValidator,
130    named: &[NamedValidationDependency<'_>],
131    ordered: &BoundValidationContext<'_>,
132) {
133    let value = ValidationValue::Text("sample");
134    assert!(
135        matches!(bound.validate_named(value, named), Ok(ValidationOutcome::Valid)),
136        "named benchmark setup must validate"
137    );
138    assert!(
139        matches!(bound.validate(value, ordered), Ok(ValidationOutcome::Valid)),
140        "ordered benchmark setup must validate"
141    );
142    let mut call_named = || {
143        let _ = black_box(black_box(bound).validate_named(black_box(value), black_box(named)));
144    };
145    let mut call_ordered = || {
146        let _ = black_box(black_box(bound).validate(black_box(value), black_box(ordered)));
147    };
148    ns_per_call(100_000, &mut call_named);
149    ns_per_call(100_000, &mut call_ordered);
150    let mut named_samples = Vec::new();
151    let mut ordered_samples = Vec::new();
152    for round in 0..5 {
153        if round % 2 == 0 {
154            named_samples.push(ns_per_call(1_000_000, &mut call_named));
155            ordered_samples.push(ns_per_call(1_000_000, &mut call_ordered));
156        } else {
157            ordered_samples.push(ns_per_call(1_000_000, &mut call_ordered));
158            named_samples.push(ns_per_call(1_000_000, &mut call_named));
159        }
160    }
161    println!(
162        "{label}: named={named_samples:?} ordered={ordered_samples:?} median_named={} median_ordered={}",
163        median(&named_samples),
164        median(&ordered_samples),
165    );
166}
Source

pub fn validate_named( &self, value: ValidationValue<'_>, dependencies: &[NamedValidationDependency<'_>], ) -> Result<ValidationOutcome, ExecutionError>

Validates a value after resolving dependency values by signature name.

This entry point protects direct callers from swapping dependencies that have the same input type. Values are reordered into signature slot order for this call. Use Self::validate when a caller has already checked and constructed values in that order. Zero or one dependency needs no reordering container; two to eight use fixed-size stack buffers, while more than eight allocate vectors as needed. Cloning a supplied non-root path may still allocate.

§Parameters
  • value: Borrowed target value.
  • dependencies: Borrowed dependency values keyed by signature name. Every declared slot must be present; optional values can use ValidationValue::Missing.
§Returns

The bound validation outcome.

§Errors

Returns a named-binding, input-shape, dependency-shape, adapter, or rule execution error.

Examples found in repository?
examples/local_registry.rs (line 133)
123fn assert_dependency_specs_come_from_signature() -> Result<(), ExecutionError> {
124    let bound = DEPENDENCY_DESCRIPTOR
125        .bind(ValidatorId::new("text.dependent"), 0, &[])
126        .expect("signature binds");
127    assert_eq!(bound.dependency_specs(), DEPENDENCIES);
128    let dependencies = [
129        NamedValidationDependency::new("maximum", ValidationValue::Text("maximum")),
130        NamedValidationDependency::new("minimum", ValidationValue::Text("minimum")),
131    ];
132    assert_eq!(
133        bound.validate_named(ValidationValue::Text("target"), &dependencies)?,
134        ValidationOutcome::Valid,
135    );
136    Ok(())
137}
More examples
Hide additional examples
examples/validate_named_bench.rs (line 135)
127fn run_case(
128    label: &str,
129    bound: &BoundValidator,
130    named: &[NamedValidationDependency<'_>],
131    ordered: &BoundValidationContext<'_>,
132) {
133    let value = ValidationValue::Text("sample");
134    assert!(
135        matches!(bound.validate_named(value, named), Ok(ValidationOutcome::Valid)),
136        "named benchmark setup must validate"
137    );
138    assert!(
139        matches!(bound.validate(value, ordered), Ok(ValidationOutcome::Valid)),
140        "ordered benchmark setup must validate"
141    );
142    let mut call_named = || {
143        let _ = black_box(black_box(bound).validate_named(black_box(value), black_box(named)));
144    };
145    let mut call_ordered = || {
146        let _ = black_box(black_box(bound).validate(black_box(value), black_box(ordered)));
147    };
148    ns_per_call(100_000, &mut call_named);
149    ns_per_call(100_000, &mut call_ordered);
150    let mut named_samples = Vec::new();
151    let mut ordered_samples = Vec::new();
152    for round in 0..5 {
153        if round % 2 == 0 {
154            named_samples.push(ns_per_call(1_000_000, &mut call_named));
155            ordered_samples.push(ns_per_call(1_000_000, &mut call_ordered));
156        } else {
157            ordered_samples.push(ns_per_call(1_000_000, &mut call_ordered));
158            named_samples.push(ns_per_call(1_000_000, &mut call_named));
159        }
160    }
161    println!(
162        "{label}: named={named_samples:?} ordered={ordered_samples:?} median_named={} median_ordered={}",
163        median(&named_samples),
164        median(&ordered_samples),
165    );
166}
Source

pub const fn input_type(&self) -> InputType

Returns the selected input shape.

Source

pub const fn dependency_specs(&self) -> &'static [DependencySpec]

Returns dependencies in their execution slot order.

Examples found in repository?
examples/local_registry.rs (line 127)
123fn assert_dependency_specs_come_from_signature() -> Result<(), ExecutionError> {
124    let bound = DEPENDENCY_DESCRIPTOR
125        .bind(ValidatorId::new("text.dependent"), 0, &[])
126        .expect("signature binds");
127    assert_eq!(bound.dependency_specs(), DEPENDENCIES);
128    let dependencies = [
129        NamedValidationDependency::new("maximum", ValidationValue::Text("maximum")),
130        NamedValidationDependency::new("minimum", ValidationValue::Text("minimum")),
131    ];
132    assert_eq!(
133        bound.validate_named(ValidationValue::Text("target"), &dependencies)?,
134        ValidationOutcome::Valid,
135    );
136    Ok(())
137}
Source

pub const fn rule_id(&self) -> ValidatorId

Returns the stable rule identifier.

Trait Implementations§

Source§

impl Clone for BoundValidator

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for BoundValidator

Source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats structural metadata without exposing the prepared implementation.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.