Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 69bbc9f

Browse files
committed
cleanup and document slots better
1 parent 7da1a6a commit 69bbc9f

3 files changed

Lines changed: 27 additions & 25 deletions

File tree

src/validators/mod.rs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ impl SchemaValidator {
6767

6868
let mut build_context = BuildContext::default();
6969
let (mut validator, _) = build_validator(schema, config, &mut build_context)?;
70-
build_context.complete_validators()?;
7170
validator.complete(&build_context)?;
7271
let slots = build_context.into_slots()?;
7372
let title = validator.get_name().into_py(py);
@@ -459,65 +458,68 @@ pub trait Validator: Send + Sync + Clone + Debug {
459458
}
460459
}
461460

461+
/// `BuildContext` is used to store extra information while building validators,
462+
/// currently it just holds a vec "slots" which holds validators need to be accessed from multiple other validators
463+
/// and therefore can't be owned by them directly.
462464
#[derive(Default, Clone)]
463465
pub struct BuildContext {
464466
slots: Vec<(String, Option<CombinedValidator>)>,
465467
}
466468

467469
impl BuildContext {
470+
/// First of two part process to add a new validator slot, we add the `slot_ref` to the array, but not the
471+
/// actual `validator`, we can't add the validator until it's build.
472+
/// We need the `id` to build the validator, hence this two-step process.
468473
pub fn prepare_slot(&mut self, slot_ref: String) -> PyResult<usize> {
469474
let id = self.slots.len();
470475
self.slots.push((slot_ref, None));
471476
Ok(id)
472477
}
473478

479+
/// Second part of adding a validator - we update the slot to include a validator
474480
pub fn complete_slot(&mut self, slot_id: usize, validator: CombinedValidator) -> PyResult<()> {
475481
match self.slots.get(slot_id) {
476482
Some((val_ref, _)) => {
477483
self.slots[slot_id] = (val_ref.clone(), Some(validator));
478484
Ok(())
479485
}
480-
None => py_error!("Recursive reference error: slot {} not found", slot_id),
486+
None => py_error!("Slots Error: slot {} not found", slot_id),
481487
}
482488
}
483489

484-
pub fn find_slot_id(&self, val_ref: &str) -> PyResult<usize> {
485-
let is_match = |(slot_ref, _): &(String, Option<CombinedValidator>)| slot_ref == val_ref;
490+
/// find a slot by `slot_ref` - iterate over the slots until we find a matching reference - return the index
491+
pub fn find_slot_id(&self, slot_ref: &str) -> PyResult<usize> {
492+
let is_match = |(match_sr, _): &(String, Option<CombinedValidator>)| match_sr == slot_ref;
486493
match self.slots.iter().position(is_match) {
487494
Some(id) => Ok(id),
488-
None => py_error!("Recursive reference error: ref '{}' not found", val_ref),
495+
None => py_error!("Slots Error: ref '{}' not found", slot_ref),
489496
}
490497
}
491498

492-
pub fn get_validator(&self, slot_id: usize) -> PyResult<&CombinedValidator> {
499+
/// find a validator by `slot_id` - this used in `Validator.complete`, specifically `RecursiveRefValidator`
500+
/// to set its name
501+
pub fn find_validator(&self, slot_id: usize) -> PyResult<&CombinedValidator> {
493502
match self.slots.get(slot_id) {
494503
Some((_, op_validator)) => match op_validator {
495504
Some(ref validator) => Ok(validator),
496-
None => py_error!("Recursive reference error: slot {} not yet filled", slot_id),
505+
None => py_error!("Slots Error: slot {} not yet filled", slot_id),
497506
},
498-
None => py_error!("Recursive reference error: slot {} not found", slot_id),
507+
None => py_error!("Slots Error: slot {} not found", slot_id),
499508
}
500509
}
501510

502-
pub fn complete_validators(&mut self) -> PyResult<()> {
503-
let self_clone = self.clone();
504-
for (_, op_validator) in self.slots.iter_mut() {
505-
match op_validator {
506-
Some(ref mut validator) => {
507-
validator.complete(&self_clone)?;
508-
}
509-
None => return py_error!("Recursive reference error: slot not yet filled"),
510-
}
511-
}
512-
Ok(())
513-
}
514-
511+
/// Move validators into a new vec which maintains the order of slots, `complete` is called on each validator
512+
/// at the same time.
515513
pub fn into_slots(self) -> PyResult<Vec<CombinedValidator>> {
514+
let self_clone = self.clone();
516515
self.slots
517516
.into_iter()
518517
.map(|(_, opt_validator)| match opt_validator {
519-
Some(validator) => Ok(validator),
520-
None => py_error!("Recursive schema build error: slot not yet filled"),
518+
Some(mut validator) => {
519+
validator.complete(&self_clone)?;
520+
Ok(validator)
521+
}
522+
None => py_error!("Slots Error: slot not yet filled"),
521523
})
522524
.collect()
523525
}

src/validators/recursive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl Validator for RecursiveRefValidator {
8888

8989
/// don't need to call complete on the inner validator here, complete_validators takes care of that.
9090
fn complete(&mut self, build_context: &BuildContext) -> PyResult<()> {
91-
let validator = build_context.get_validator(self.validator_id)?;
91+
let validator = build_context.find_validator(self.validator_id)?;
9292
self.inner_name = validator.get_name().to_string();
9393
Ok(())
9494
}

tests/validators/test_recursive.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ class Branch:
200200

201201

202202
def test_invalid_schema():
203-
with pytest.raises(SchemaError, match="Recursive reference error: ref 'Branch' not found"):
203+
with pytest.raises(SchemaError, match="Slots Error: ref 'Branch' not found"):
204204
SchemaValidator(
205205
{
206206
'type': 'list',

0 commit comments

Comments
 (0)