Skip to content

Commit

Permalink
Merge pull request #2364 from hannobraun/validation
Browse files Browse the repository at this point in the history
Continue cleanup of reference-counting validation code
  • Loading branch information
hannobraun authored May 24, 2024
2 parents 3d633f6 + 3c09fe5 commit e861a51
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 113 deletions.
5 changes: 4 additions & 1 deletion crates/fj-core/src/validate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ use crate::{
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
/// pattern. This is preferred to matching on [`Validate::validate_and_return_first_error`], since usually we don't care about the order.
Expand Down
95 changes: 34 additions & 61 deletions crates/fj-core/src/validate/references.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
use std::{any::type_name_of_val, collections::HashMap, fmt};

use crate::{
storage::Handle,
topology::{Cycle, Face, HalfEdge, Region, Shell},
};
use crate::storage::Handle;

/// Object that should be exclusively owned by another, is not
///
/// Some objects are expected to be "owned" by a single other object. This means
/// that only one reference to these objects must exist within the topological
/// object graph.
#[derive(Clone, Debug, thiserror::Error)]
pub struct ObjectNotExclusivelyOwned<T, U> {
object: Handle<T>,
referenced_by: Vec<Handle<U>>,
}

impl<T, U> fmt::Display for ObjectNotExclusivelyOwned<T, U>
where
T: fmt::Debug,
U: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"`{}` ({:?}) referenced by multiple `{}` objects ({:?})",
type_name_of_val(&self.object),
self.object,
type_name_of_val(&self.referenced_by),
self.referenced_by
)
}
}

#[derive(Default)]
pub struct ReferenceCounter<T, U>(HashMap<Handle<T>, Vec<Handle<U>>>);
Expand All @@ -17,11 +42,11 @@ impl<T, U> ReferenceCounter<T, U> {
self.0.entry(to).or_default().push(from);
}

pub fn find_multiples(&self) -> Vec<MultipleReferences<T, U>> {
pub fn find_multiples(&self) -> Vec<ObjectNotExclusivelyOwned<T, U>> {
self.0
.iter()
.filter(|(_, referenced_by)| referenced_by.len() > 1)
.map(|(object, referenced_by)| MultipleReferences {
.map(|(object, referenced_by)| ObjectNotExclusivelyOwned {
object: object.clone(),
referenced_by: referenced_by.to_vec(),
})
Expand All @@ -32,64 +57,12 @@ impl<T, U> ReferenceCounter<T, U> {
/// Find errors and convert to [`crate::validate::ValidationError`]
#[macro_export]
macro_rules! validate_references {
($errors:ident, $error_ty:ty;$($counter:ident, $err:ident;)*) => {
($errors:ident;$($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());
let reference_error = ValidationError::$err(multiple.clone());
$errors.push(reference_error.into());
});
)*
};
}

/// Object that should be exclusively owned by another, is not
///
/// Some objects are expected to be "owned" by a single other object. This means
/// that only one reference to these objects must exist within the topological
/// object graph.
#[derive(Clone, Debug, thiserror::Error)]
pub enum ObjectNotExclusivelyOwned {
/// [`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>,
referenced_by: Vec<Handle<U>>,
}

impl<T, U> fmt::Display for MultipleReferences<T, U>
where
T: fmt::Debug,
U: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"`{}` ({:?}) referenced by multiple `{}` objects ({:?})",
type_name_of_val(&self.object),
self.object,
type_name_of_val(&self.referenced_by),
self.referenced_by
)
}
}
30 changes: 7 additions & 23 deletions crates/fj-core/src/validate/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use crate::{
};

use super::{
references::{ObjectNotExclusivelyOwned, ReferenceCounter},
Validate, ValidationConfig, ValidationError,
references::ReferenceCounter, Validate, ValidationConfig, ValidationError,
};

impl Validate for Sketch {
Expand All @@ -37,10 +36,6 @@ impl Validate for Sketch {
/// [`Sketch`] validation failed
#[derive(Clone, Debug, thiserror::Error)]
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
#[error(
"Exterior cycle within sketch region has clockwise winding\n
Expand Down Expand Up @@ -81,9 +76,9 @@ impl SketchValidationError {
});

validate_references!(
errors, SketchValidationError;
referenced_edges, HalfEdge;
referenced_cycles, Cycle;
errors;
referenced_edges, MultipleReferencesToHalfEdge;
referenced_cycles, MultipleReferencesToCycle;
);
}

Expand Down Expand Up @@ -135,10 +130,7 @@ mod tests {
build::BuildHalfEdge, build::BuildRegion, insert::Insert,
},
topology::{Cycle, HalfEdge, Region, Sketch, Vertex},
validate::{
references::ObjectNotExclusivelyOwned, SketchValidationError,
Validate, ValidationError,
},
validate::{SketchValidationError, Validate, ValidationError},
Core,
};

Expand Down Expand Up @@ -170,11 +162,7 @@ mod tests {
assert_contains_err!(
core,
invalid_sketch,
ValidationError::Sketch(
SketchValidationError::ObjectNotExclusivelyOwned(
ObjectNotExclusivelyOwned::Cycle { references: _ }
)
)
ValidationError::MultipleReferencesToCycle(_)
);

Ok(())
Expand Down Expand Up @@ -210,11 +198,7 @@ mod tests {
assert_contains_err!(
core,
invalid_sketch,
ValidationError::Sketch(
SketchValidationError::ObjectNotExclusivelyOwned(
ObjectNotExclusivelyOwned::HalfEdge { references: _ }
)
)
ValidationError::MultipleReferencesToHalfEdge(_)
);

Ok(())
Expand Down
38 changes: 11 additions & 27 deletions crates/fj-core/src/validate/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use crate::{
use fj_math::Point;

use super::{
references::{ObjectNotExclusivelyOwned, ReferenceCounter},
Validate, ValidationConfig, ValidationError,
references::ReferenceCounter, Validate, ValidationConfig, ValidationError,
};

impl Validate for Solid {
Expand Down Expand Up @@ -67,10 +66,6 @@ pub enum SolidValidationError {
/// Position of second vertex
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 {
Expand Down Expand Up @@ -168,11 +163,11 @@ impl SolidValidationError {
});

validate_references!(
errors, SolidValidationError;
referenced_regions, Region;
referenced_faces, Face;
referenced_edges, HalfEdge;
referenced_cycles, Cycle;
errors;
referenced_regions, MultipleReferencesToRegion;
referenced_faces, MultipleReferencesToFace;
referenced_edges, MultipleReferencesToHalfEdge;
referenced_cycles, MultipleReferencesToCycle;
);
}
}
Expand All @@ -187,10 +182,7 @@ mod tests {
insert::Insert,
},
topology::{Cycle, Face, HalfEdge, Region, Shell, Solid, Surface},
validate::{
references::ObjectNotExclusivelyOwned, SolidValidationError,
Validate, ValidationError,
},
validate::{Validate, ValidationError},
Core,
};

Expand Down Expand Up @@ -238,9 +230,7 @@ mod tests {
assert_contains_err!(
core,
invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences(
ObjectNotExclusivelyOwned::Face { references: _ }
))
ValidationError::MultipleReferencesToFace(_)
);

let valid_solid = Solid::new(vec![]).insert(&mut core);
Expand Down Expand Up @@ -284,9 +274,7 @@ mod tests {
assert_contains_err!(
core,
invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences(
ObjectNotExclusivelyOwned::Region { references: _ }
))
ValidationError::MultipleReferencesToRegion(_)
);

let valid_solid = Solid::new(vec![]).insert(&mut core);
Expand Down Expand Up @@ -334,9 +322,7 @@ mod tests {
assert_contains_err!(
core,
invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences(
ObjectNotExclusivelyOwned::Cycle { references: _ }
))
ValidationError::MultipleReferencesToCycle(_)
);

let valid_solid = Solid::new(vec![]).insert(&mut core);
Expand Down Expand Up @@ -376,9 +362,7 @@ mod tests {
assert_contains_err!(
core,
invalid_solid,
ValidationError::Solid(SolidValidationError::MultipleReferences(
ObjectNotExclusivelyOwned::HalfEdge { references: _ }
))
ValidationError::MultipleReferencesToHalfEdge(_)
);

let valid_solid = Solid::new(vec![]).insert(&mut core);
Expand Down
23 changes: 22 additions & 1 deletion crates/fj-core/src/validation/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
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::{
AdjacentHalfEdgesNotConnected, CoincidentHalfEdgesAreNotSiblings,
Expand Down Expand Up @@ -37,6 +42,22 @@ pub enum ValidationError {
#[error(transparent)]
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
#[error("`Solid` validation error")]
Solid(#[from] SolidValidationError),
Expand Down

0 comments on commit e861a51

Please sign in to comment.