Skip to content

Commit 75e6a68

Browse files
authored
Merge pull request #2213 from hannobraun/layers
Clean up the layers code
2 parents b01a209 + 2ca47a7 commit 75e6a68

File tree

9 files changed

+183
-152
lines changed

9 files changed

+183
-152
lines changed

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ categories = ["encoding", "mathematics", "rendering"]
5151
[workspace.lints.rust]
5252
missing_docs = "warn"
5353

54+
[workspace.lints.clippy]
55+
# I really don't see any point in this. It can make a public API ugly, but
56+
# a) that will be obvious, even without a lint, and
57+
# b) it provides benefits in private APIs with top-level re-exports.
58+
module_inception = "allow"
59+
5460

5561
[workspace.dependencies.fj]
5662
version = "0.48.0"

crates/fj-core/src/layers/layers.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use crate::{
2+
objects::Objects,
3+
validate::{Validation, ValidationConfig},
4+
};
5+
6+
use super::Layer;
7+
8+
/// # Loosely coupled layers, that together define shapes
9+
///
10+
/// Shapes are not a monolithic thing in Fornjot, but instead are defined by
11+
/// several, loosely coupled layers. These layers are owned by this struct.
12+
///
13+
/// ## Implementation Note
14+
///
15+
/// It is totally conceivable that one day, this system of layers is extensible
16+
/// and more layers can be defined by third-party code. The foundation for that,
17+
/// the loose coupling and inter-layer communication via events, is already
18+
/// there, conceptually.
19+
///
20+
/// For now, there is no need for this, and all layers are just hardcoded here.
21+
/// That can be changed, once necessary.
22+
#[derive(Default)]
23+
pub struct Layers {
24+
/// The objects layers
25+
///
26+
/// Manages the stores of topological and geometric objects that make up
27+
/// shapes.
28+
pub objects: Layer<Objects>,
29+
30+
/// The validation layer
31+
///
32+
/// Monitors objects and validates them, as they are inserted.
33+
pub validation: Layer<Validation>,
34+
}
35+
36+
impl Layers {
37+
/// Construct an instance of `Layers`
38+
pub fn new() -> Self {
39+
Self::default()
40+
}
41+
42+
/// Construct an instance of `Layers`, using the provided configuration
43+
pub fn with_validation_config(config: ValidationConfig) -> Self {
44+
Self {
45+
validation: Layer::new(Validation::with_validation_config(config)),
46+
..Default::default()
47+
}
48+
}
49+
}

crates/fj-core/src/layers/mod.rs

+5-80
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,13 @@
22
//!
33
//! See [`Layers`].
44
5-
mod layer;
6-
mod objects;
7-
mod validation;
5+
pub mod objects;
6+
pub mod validation;
87

9-
use crate::{
10-
objects::{AboutToBeStored, AnyObject, Objects},
11-
validate::{ValidationConfig, ValidationErrors},
12-
};
8+
mod layer;
9+
mod layers;
1310

1411
pub use self::{
1512
layer::{Layer, State},
16-
objects::{InsertObject, Operation},
17-
validation::{Validation, ValidationCommand, ValidationEvent},
13+
layers::Layers,
1814
};
19-
20-
/// # Loosely coupled layers, that together define shapes
21-
///
22-
/// Shapes are not a monolithic thing in Fornjot, but instead are defined by
23-
/// several, loosely coupled layers. These layers are owned by this struct.
24-
///
25-
/// ## Implementation Note
26-
///
27-
/// It is totally conceivable that one day, this system of layers is extensible
28-
/// and more layers can be defined by third-party code. The foundation for that,
29-
/// the loose coupling and inter-layer communication via events, is already
30-
/// there, conceptually.
31-
///
32-
/// For now, there is no need for this, and all layers are just hardcoded here.
33-
/// That can be changed, once necessary.
34-
#[derive(Default)]
35-
pub struct Layers {
36-
/// The objects layers
37-
///
38-
/// Manages the stores of topological and geometric objects that make up
39-
/// shapes.
40-
pub objects: Layer<Objects>,
41-
42-
/// The validation layer
43-
///
44-
/// Monitors objects and validates them, as they are inserted.
45-
pub validation: Layer<Validation>,
46-
}
47-
48-
impl Layers {
49-
/// Construct an instance of `Layers`
50-
pub fn new() -> Self {
51-
Self::default()
52-
}
53-
54-
/// Construct an instance of `Layers`, using the provided configuration
55-
pub fn with_validation_config(config: ValidationConfig) -> Self {
56-
let objects = Layer::default();
57-
let validation = Layer::new(Validation::with_validation_config(config));
58-
59-
Self {
60-
objects,
61-
validation,
62-
}
63-
}
64-
65-
/// Insert an object into the stores
66-
pub fn insert_object(&mut self, object: AnyObject<AboutToBeStored>) {
67-
let mut object_events = Vec::new();
68-
self.objects
69-
.process(Operation::InsertObject { object }, &mut object_events);
70-
71-
for object_event in object_events {
72-
let command = ValidationCommand::ValidateObject {
73-
object: object_event.object.into(),
74-
};
75-
self.validation.process(command, &mut Vec::new());
76-
}
77-
}
78-
79-
/// Drop `Layers`; return any unhandled validation error
80-
pub fn drop_and_validate(self) -> Result<(), ValidationErrors> {
81-
let errors = self.validation.into_state().into_errors();
82-
83-
if errors.0.is_empty() {
84-
Ok(())
85-
} else {
86-
Err(errors)
87-
}
88-
}
89-
}

crates/fj-core/src/layers/objects.rs

+36-11
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
1-
use crate::objects::{AboutToBeStored, AnyObject, Objects};
1+
//! Layer infrastructure for [`Objects`]
22
3-
use super::State;
3+
use crate::{
4+
objects::{AboutToBeStored, AnyObject, Objects},
5+
validate::Validation,
6+
};
7+
8+
use super::{Layer, State};
9+
10+
impl Layer<Objects> {
11+
/// Insert and object into the stores
12+
pub fn insert(
13+
&mut self,
14+
object: AnyObject<AboutToBeStored>,
15+
validation: &mut Layer<Validation>,
16+
) {
17+
let mut events = Vec::new();
18+
self.process(ObjectsCommand::InsertObject { object }, &mut events);
19+
20+
for event in events {
21+
validation.on_objects_event(event);
22+
}
23+
}
24+
}
425

526
impl State for Objects {
6-
type Command = Operation;
7-
type Event = InsertObject;
27+
type Command = ObjectsCommand;
28+
type Event = ObjectsEvent;
829

930
fn decide(&self, command: Self::Command, events: &mut Vec<Self::Event>) {
10-
let Operation::InsertObject { object } = command;
11-
events.push(InsertObject { object });
31+
let ObjectsCommand::InsertObject { object } = command;
32+
events.push(ObjectsEvent::InsertObject { object });
1233
}
1334

1435
fn evolve(&mut self, event: &Self::Event) {
15-
event.object.clone().insert(self);
36+
let ObjectsEvent::InsertObject { object } = event;
37+
object.clone().insert(self);
1638
}
1739
}
1840

1941
/// Command for `Layer<Objects>`
2042
#[derive(Debug)]
21-
pub enum Operation {
43+
pub enum ObjectsCommand {
2244
/// Insert an object into the stores
2345
///
2446
/// This is the one primitive operation that all other operations are built
@@ -31,7 +53,10 @@ pub enum Operation {
3153

3254
/// Event produced by `Layer<Objects>`
3355
#[derive(Clone, Debug)]
34-
pub struct InsertObject {
35-
/// The object to insert
36-
pub object: AnyObject<AboutToBeStored>,
56+
pub enum ObjectsEvent {
57+
/// Insert an object into the stores
58+
InsertObject {
59+
/// The object to insert
60+
object: AnyObject<AboutToBeStored>,
61+
},
3762
}

crates/fj-core/src/layers/validation.rs

+18-50
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,30 @@
1-
use std::{collections::HashMap, error::Error, thread};
1+
//! Layer infrastructure for [`Validation`]
22
33
use crate::{
44
objects::{AnyObject, Stored},
5-
storage::ObjectId,
6-
validate::{ValidationConfig, ValidationError, ValidationErrors},
5+
validate::{Validation, ValidationError, ValidationErrors},
76
};
87

9-
use super::State;
8+
use super::{objects::ObjectsEvent, Layer, State};
109

11-
/// Errors that occurred while validating the objects inserted into the stores
12-
#[derive(Default)]
13-
pub struct Validation {
14-
/// All unhandled validation errors
15-
errors: HashMap<ObjectId, ValidationError>,
16-
17-
/// Validation configuration for the validation service
18-
config: ValidationConfig,
19-
}
20-
21-
impl Validation {
22-
/// Construct an instance of `Validation`, using the provided configuration
23-
pub fn with_validation_config(config: ValidationConfig) -> Self {
24-
let errors = HashMap::new();
25-
Self { errors, config }
10+
impl Layer<Validation> {
11+
/// Handler for [`ObjectsEvent`]
12+
pub fn on_objects_event(&mut self, event: ObjectsEvent) {
13+
let ObjectsEvent::InsertObject { object } = event;
14+
let command = ValidationCommand::ValidateObject {
15+
object: object.into(),
16+
};
17+
self.process(command, &mut Vec::new());
2618
}
2719

28-
/// Drop this instance, returning the errors it contained
29-
pub fn into_errors(mut self) -> ValidationErrors {
30-
ValidationErrors(self.errors.drain().map(|(_, error)| error).collect())
31-
}
32-
}
33-
34-
impl Drop for Validation {
35-
fn drop(&mut self) {
36-
let num_errors = self.errors.len();
37-
if num_errors > 0 {
38-
println!(
39-
"Dropping `Validation` with {num_errors} unhandled validation \
40-
errors:"
41-
);
20+
/// Consume the validation layer, returning any validation errors
21+
pub fn into_result(self) -> Result<(), ValidationErrors> {
22+
let errors = self.into_state().into_errors();
4223

43-
for err in self.errors.values() {
44-
println!("{}", err);
45-
46-
// Once `Report` is stable, we can replace this:
47-
// https://doc.rust-lang.org/std/error/struct.Report.html
48-
let mut source = err.source();
49-
while let Some(err) = source {
50-
println!("\nCaused by:\n\t{err}");
51-
source = err.source();
52-
}
53-
54-
print!("\n\n");
55-
}
56-
57-
if !thread::panicking() {
58-
panic!();
59-
}
24+
if errors.0.is_empty() {
25+
Ok(())
26+
} else {
27+
Err(errors)
6028
}
6129
}
6230
}

crates/fj-core/src/operations/insert/insert_trait.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ macro_rules! impl_insert {
4040
fn insert(self, core: &mut Instance) -> Self::Inserted {
4141
let handle = core.layers.objects.$store.reserve();
4242
let object = (handle.clone(), self).into();
43-
core.layers.insert_object(object);
43+
core.layers.objects.insert(
44+
object,
45+
&mut core.layers.validation,
46+
);
4447
handle
4548
}
4649
}

crates/fj-core/src/validate/mod.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,17 @@ mod solid;
7676
mod surface;
7777
mod vertex;
7878

79+
use crate::storage::ObjectId;
80+
7981
pub use self::{
8082
cycle::CycleValidationError, edge::EdgeValidationError,
8183
face::FaceValidationError, shell::ShellValidationError,
8284
sketch::SketchValidationError, solid::SolidValidationError,
8385
};
8486

85-
use std::{convert::Infallible, fmt};
87+
use std::{
88+
collections::HashMap, convert::Infallible, error::Error, fmt, thread,
89+
};
8690

8791
use fj_math::Scalar;
8892

@@ -99,6 +103,59 @@ macro_rules! assert_contains_err {
99103
};
100104
}
101105

106+
/// Errors that occurred while validating the objects inserted into the stores
107+
#[derive(Default)]
108+
pub struct Validation {
109+
/// All unhandled validation errors
110+
pub errors: HashMap<ObjectId, ValidationError>,
111+
112+
/// Validation configuration for the validation service
113+
pub config: ValidationConfig,
114+
}
115+
116+
impl Validation {
117+
/// Construct an instance of `Validation`, using the provided configuration
118+
pub fn with_validation_config(config: ValidationConfig) -> Self {
119+
let errors = HashMap::new();
120+
Self { errors, config }
121+
}
122+
123+
/// Drop this instance, returning the errors it contained
124+
pub fn into_errors(mut self) -> ValidationErrors {
125+
ValidationErrors(self.errors.drain().map(|(_, error)| error).collect())
126+
}
127+
}
128+
129+
impl Drop for Validation {
130+
fn drop(&mut self) {
131+
let num_errors = self.errors.len();
132+
if num_errors > 0 {
133+
println!(
134+
"Dropping `Validation` with {num_errors} unhandled validation \
135+
errors:"
136+
);
137+
138+
for err in self.errors.values() {
139+
println!("{}", err);
140+
141+
// Once `Report` is stable, we can replace this:
142+
// https://doc.rust-lang.org/std/error/struct.Report.html
143+
let mut source = err.source();
144+
while let Some(err) = source {
145+
println!("\nCaused by:\n\t{err}");
146+
source = err.source();
147+
}
148+
149+
print!("\n\n");
150+
}
151+
152+
if !thread::panicking() {
153+
panic!();
154+
}
155+
}
156+
}
157+
}
158+
102159
/// Validate an object
103160
///
104161
/// This trait is used automatically when inserting an object into a store.

0 commit comments

Comments
 (0)