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
impl BoundValidator
Sourcepub fn try_from_prepared<T: 'static>(
rule_id: ValidatorId,
prepared: Arc<dyn PreparedValidator>,
) -> Result<Self, BindError>
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.
Sourcepub fn validate(
&self,
value: ValidationValue<'_>,
context: &BoundValidationContext<'_>,
) -> Result<ValidationOutcome, ExecutionError>
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?
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
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}Sourcepub fn validate_named(
&self,
value: ValidationValue<'_>,
dependencies: &[NamedValidationDependency<'_>],
) -> Result<ValidationOutcome, ExecutionError>
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 useValidationValue::Missing.
§Returns
The bound validation outcome.
§Errors
Returns a named-binding, input-shape, dependency-shape, adapter, or rule execution error.
Examples found in repository?
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
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}Sourcepub const fn input_type(&self) -> InputType
pub const fn input_type(&self) -> InputType
Returns the selected input shape.
Sourcepub const fn dependency_specs(&self) -> &'static [DependencySpec]
pub const fn dependency_specs(&self) -> &'static [DependencySpec]
Returns dependencies in their execution slot order.
Examples found in repository?
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}Sourcepub const fn rule_id(&self) -> ValidatorId
pub const fn rule_id(&self) -> ValidatorId
Returns the stable rule identifier.