Merge pull request #2364 from hannobraun/validation

Continue cleanup of reference-counting validation code
This commit is contained in:
Hanno Braun 2024-05-24 22:41:00 +02:00 committed by GitHub
commit e861a511bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 84 additions and 119 deletions

View File

@ -78,7 +78,10 @@ use crate::{
validation::{ValidationConfig, ValidationError}, validation::{ValidationConfig, ValidationError},
}; };
pub use self::{sketch::SketchValidationError, solid::SolidValidationError}; pub use self::{
references::ObjectNotExclusivelyOwned, sketch::SketchValidationError,
solid::SolidValidationError,
};
/// Assert that some object has a validation error which matches a specific /// Assert that some object has a validation error which matches a specific
/// pattern. This is preferred to matching on [`Validate::validate_and_return_first_error`], since usually we don't care about the order. /// pattern. This is preferred to matching on [`Validate::validate_and_return_first_error`], since usually we don't care about the order.

View File

@ -1,46 +1,6 @@
use std::{any::type_name_of_val, collections::HashMap, fmt}; use std::{any::type_name_of_val, collections::HashMap, fmt};
use crate::{ use crate::storage::Handle;
storage::Handle,
topology::{Cycle, Face, HalfEdge, Region, Shell},
};
#[derive(Default)]
pub struct ReferenceCounter<T, U>(HashMap<Handle<T>, Vec<Handle<U>>>);
impl<T, U> ReferenceCounter<T, U> {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn count_reference(&mut self, to: Handle<T>, from: Handle<U>) {
self.0.entry(to).or_default().push(from);
}
pub fn find_multiples(&self) -> Vec<MultipleReferences<T, U>> {
self.0
.iter()
.filter(|(_, referenced_by)| referenced_by.len() > 1)
.map(|(object, referenced_by)| MultipleReferences {
object: object.clone(),
referenced_by: referenced_by.to_vec(),
})
.collect()
}
}
/// Find errors and convert to [`crate::validate::ValidationError`]
#[macro_export]
macro_rules! validate_references {
($errors:ident, $error_ty:ty;$($counter:ident, $err:ident;)*) => {
$(
$counter.find_multiples().iter().for_each(|multiple| {
let reference_error = ObjectNotExclusivelyOwned::$err { references: multiple.clone() };
$errors.push(Into::<$error_ty>::into(reference_error).into());
});
)*
};
}
/// Object that should be exclusively owned by another, is not /// Object that should be exclusively owned by another, is not
/// ///
@ -48,36 +8,12 @@ macro_rules! validate_references {
/// that only one reference to these objects must exist within the topological /// that only one reference to these objects must exist within the topological
/// object graph. /// object graph.
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
pub enum ObjectNotExclusivelyOwned { pub struct ObjectNotExclusivelyOwned<T, U> {
/// [`Region`] referenced by more than one [`Face`]
#[error(transparent)]
Region {
references: MultipleReferences<Region, Face>,
},
/// [`Face`] referenced by more than one [`Shell`]
#[error(transparent)]
Face {
references: MultipleReferences<Face, Shell>,
},
/// [`HalfEdge`] referenced by more than one [`Cycle`]
#[error(transparent)]
HalfEdge {
references: MultipleReferences<HalfEdge, Cycle>,
},
/// [`Cycle`] referenced by more than one [`Region`]
#[error(transparent)]
Cycle {
references: MultipleReferences<Cycle, Region>,
},
}
#[derive(Clone, Debug, thiserror::Error)]
pub struct MultipleReferences<T, U> {
object: Handle<T>, object: Handle<T>,
referenced_by: Vec<Handle<U>>, referenced_by: Vec<Handle<U>>,
} }
impl<T, U> fmt::Display for MultipleReferences<T, U> impl<T, U> fmt::Display for ObjectNotExclusivelyOwned<T, U>
where where
T: fmt::Debug, T: fmt::Debug,
U: fmt::Debug, U: fmt::Debug,
@ -93,3 +29,40 @@ where
) )
} }
} }
#[derive(Default)]
pub struct ReferenceCounter<T, U>(HashMap<Handle<T>, Vec<Handle<U>>>);
impl<T, U> ReferenceCounter<T, U> {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn count_reference(&mut self, to: Handle<T>, from: Handle<U>) {
self.0.entry(to).or_default().push(from);
}
pub fn find_multiples(&self) -> Vec<ObjectNotExclusivelyOwned<T, U>> {
self.0
.iter()
.filter(|(_, referenced_by)| referenced_by.len() > 1)
.map(|(object, referenced_by)| ObjectNotExclusivelyOwned {
object: object.clone(),
referenced_by: referenced_by.to_vec(),
})
.collect()
}
}
/// Find errors and convert to [`crate::validate::ValidationError`]
#[macro_export]
macro_rules! validate_references {
($errors:ident;$($counter:ident, $err:ident;)*) => {
$(
$counter.find_multiples().iter().for_each(|multiple| {
let reference_error = ValidationError::$err(multiple.clone());
$errors.push(reference_error.into());
});
)*
};
}

View File

@ -9,8 +9,7 @@ use crate::{
}; };
use super::{ use super::{
references::{ObjectNotExclusivelyOwned, ReferenceCounter}, references::ReferenceCounter, Validate, ValidationConfig, ValidationError,
Validate, ValidationConfig, ValidationError,
}; };
impl Validate for Sketch { impl Validate for Sketch {
@ -37,10 +36,6 @@ impl Validate for Sketch {
/// [`Sketch`] validation failed /// [`Sketch`] validation failed
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
pub enum SketchValidationError { pub enum SketchValidationError {
/// Object within sketch referenced by more than one other object
#[error("Object within sketch referenced by more than one other Object")]
ObjectNotExclusivelyOwned(#[from] ObjectNotExclusivelyOwned),
/// Region within sketch has exterior cycle with clockwise winding /// Region within sketch has exterior cycle with clockwise winding
#[error( #[error(
"Exterior cycle within sketch region has clockwise winding\n "Exterior cycle within sketch region has clockwise winding\n
@ -81,9 +76,9 @@ impl SketchValidationError {
}); });
validate_references!( validate_references!(
errors, SketchValidationError; errors;
referenced_edges, HalfEdge; referenced_edges, MultipleReferencesToHalfEdge;
referenced_cycles, Cycle; referenced_cycles, MultipleReferencesToCycle;
); );
} }
@ -135,10 +130,7 @@ mod tests {
build::BuildHalfEdge, build::BuildRegion, insert::Insert, build::BuildHalfEdge, build::BuildRegion, insert::Insert,
}, },
topology::{Cycle, HalfEdge, Region, Sketch, Vertex}, topology::{Cycle, HalfEdge, Region, Sketch, Vertex},
validate::{ validate::{SketchValidationError, Validate, ValidationError},
references::ObjectNotExclusivelyOwned, SketchValidationError,
Validate, ValidationError,
},
Core, Core,
}; };
@ -170,11 +162,7 @@ mod tests {
assert_contains_err!( assert_contains_err!(
core, core,
invalid_sketch, invalid_sketch,
ValidationError::Sketch( ValidationError::MultipleReferencesToCycle(_)
SketchValidationError::ObjectNotExclusivelyOwned(
ObjectNotExclusivelyOwned::Cycle { references: _ }
)
)
); );
Ok(()) Ok(())
@ -210,11 +198,7 @@ mod tests {
assert_contains_err!( assert_contains_err!(
core, core,
invalid_sketch, invalid_sketch,
ValidationError::Sketch( ValidationError::MultipleReferencesToHalfEdge(_)
SketchValidationError::ObjectNotExclusivelyOwned(
ObjectNotExclusivelyOwned::HalfEdge { references: _ }
)
)
); );
Ok(()) Ok(())

View File

@ -9,8 +9,7 @@ use crate::{
use fj_math::Point; use fj_math::Point;
use super::{ use super::{
references::{ObjectNotExclusivelyOwned, ReferenceCounter}, references::ReferenceCounter, Validate, ValidationConfig, ValidationError,
Validate, ValidationConfig, ValidationError,
}; };
impl Validate for Solid { impl Validate for Solid {
@ -67,10 +66,6 @@ pub enum SolidValidationError {
/// Position of second vertex /// Position of second vertex
position_b: Point<3>, position_b: Point<3>,
}, },
/// Object within solid referenced by more than one other object
#[error("Object within solid referenced by more than one other Object")]
MultipleReferences(#[from] ObjectNotExclusivelyOwned),
} }
impl SolidValidationError { impl SolidValidationError {
@ -168,11 +163,11 @@ impl SolidValidationError {
}); });
validate_references!( validate_references!(
errors, SolidValidationError; errors;
referenced_regions, Region; referenced_regions, MultipleReferencesToRegion;
referenced_faces, Face; referenced_faces, MultipleReferencesToFace;
referenced_edges, HalfEdge; referenced_edges, MultipleReferencesToHalfEdge;
referenced_cycles, Cycle; referenced_cycles, MultipleReferencesToCycle;
); );
} }
} }
@ -187,10 +182,7 @@ mod tests {
insert::Insert, insert::Insert,
}, },
topology::{Cycle, Face, HalfEdge, Region, Shell, Solid, Surface}, topology::{Cycle, Face, HalfEdge, Region, Shell, Solid, Surface},
validate::{ validate::{Validate, ValidationError},
references::ObjectNotExclusivelyOwned, SolidValidationError,
Validate, ValidationError,
},
Core, Core,
}; };
@ -238,9 +230,7 @@ mod tests {
assert_contains_err!( assert_contains_err!(
core, core,
invalid_solid, invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences( ValidationError::MultipleReferencesToFace(_)
ObjectNotExclusivelyOwned::Face { references: _ }
))
); );
let valid_solid = Solid::new(vec![]).insert(&mut core); let valid_solid = Solid::new(vec![]).insert(&mut core);
@ -284,9 +274,7 @@ mod tests {
assert_contains_err!( assert_contains_err!(
core, core,
invalid_solid, invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences( ValidationError::MultipleReferencesToRegion(_)
ObjectNotExclusivelyOwned::Region { references: _ }
))
); );
let valid_solid = Solid::new(vec![]).insert(&mut core); let valid_solid = Solid::new(vec![]).insert(&mut core);
@ -334,9 +322,7 @@ mod tests {
assert_contains_err!( assert_contains_err!(
core, core,
invalid_solid, invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences( ValidationError::MultipleReferencesToCycle(_)
ObjectNotExclusivelyOwned::Cycle { references: _ }
))
); );
let valid_solid = Solid::new(vec![]).insert(&mut core); let valid_solid = Solid::new(vec![]).insert(&mut core);
@ -376,9 +362,7 @@ mod tests {
assert_contains_err!( assert_contains_err!(
core, core,
invalid_solid, invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences( ValidationError::MultipleReferencesToHalfEdge(_)
ObjectNotExclusivelyOwned::HalfEdge { references: _ }
))
); );
let valid_solid = Solid::new(vec![]).insert(&mut core); let valid_solid = Solid::new(vec![]).insert(&mut core);

View File

@ -1,6 +1,11 @@
use std::{convert::Infallible, fmt}; use std::{convert::Infallible, fmt};
use crate::validate::{SketchValidationError, SolidValidationError}; use crate::{
topology::{Cycle, Face, HalfEdge, Region, Shell},
validate::{
ObjectNotExclusivelyOwned, SketchValidationError, SolidValidationError,
},
};
use super::checks::{ use super::checks::{
AdjacentHalfEdgesNotConnected, CoincidentHalfEdgesAreNotSiblings, AdjacentHalfEdgesNotConnected, CoincidentHalfEdgesAreNotSiblings,
@ -37,6 +42,22 @@ pub enum ValidationError {
#[error(transparent)] #[error(transparent)]
InteriorCycleHasInvalidWinding(#[from] InteriorCycleHasInvalidWinding), InteriorCycleHasInvalidWinding(#[from] InteriorCycleHasInvalidWinding),
/// Multiple references to [`Cycle`]
#[error(transparent)]
MultipleReferencesToCycle(ObjectNotExclusivelyOwned<Cycle, Region>),
/// Multiple references to [`Face`]
#[error(transparent)]
MultipleReferencesToFace(ObjectNotExclusivelyOwned<Face, Shell>),
/// Multiple references to [`HalfEdge`]
#[error(transparent)]
MultipleReferencesToHalfEdge(ObjectNotExclusivelyOwned<HalfEdge, Cycle>),
/// Multiple references to [`Region`]
#[error(transparent)]
MultipleReferencesToRegion(ObjectNotExclusivelyOwned<Region, Face>),
/// `Solid` validation error /// `Solid` validation error
#[error("`Solid` validation error")] #[error("`Solid` validation error")]
Solid(#[from] SolidValidationError), Solid(#[from] SolidValidationError),