es_entity/write_shape.rs
1//! Whether a repo's generated write ops can reject, as a type.
2//!
3//! A repo's `create`/`update` return `RepoWriteError<CV>` when they can hit a
4//! constraint and the plain `RepoFault` when they cannot. A parent repo's
5//! write also runs its nested children's writes, so the parent can reject if
6//! its own statement can *or* any child's can. The derive of the parent cannot
7//! see the child's options, so each repo publishes its shapes as
8//! [`WriteShapes`] on its generated constraint violation enum and the parent
9//! folds them with [`WriteShape::Or`]. The enum carries them rather than the
10//! repo type because it is always `pub`: a public parent whose signatures
11//! mention a less visible child repo would not compile.
12
13use errlanes::UnmappedInto;
14
15use crate::{Fail, Fatal, Fault, Lift, Rejection, RepoFault, RepoWriteError};
16
17/// [`Rejects`] or [`Faults`].
18pub trait WriteShape {
19 /// The error a write of this shape returns for the constraint violation
20 /// `CV`.
21 type Error<CV: Rejection>;
22 /// The shape of a write that performs a write of this shape followed by
23 /// one of shape `S`: it rejects when either does.
24 type Or<S: WriteShape>: WriteShape;
25}
26
27/// What a repo publishes, on its generated constraint violation enum, about its
28/// own writes (nested children included): `Create` for
29/// `create`/`create_all`, `Update` for `update`/`update_all`/`forget`/
30/// `delete`. A parent's update also creates its new children, so it folds in
31/// their `Create` as well.
32#[doc(hidden)]
33pub trait WriteShapes {
34 type Create: WriteShape;
35 type Update: WriteShape;
36}
37
38/// The write can hit a constraint: `RepoWriteError<CV>`.
39pub struct Rejects;
40
41/// The write can reject on nothing: `RepoFault`.
42pub struct Faults;
43
44impl WriteShape for Rejects {
45 type Error<CV: Rejection> = RepoWriteError<CV>;
46 type Or<S: WriteShape> = Rejects;
47}
48
49impl WriteShape for Faults {
50 type Error<CV: Rejection> = RepoFault;
51 type Or<S: WriteShape> = S;
52}
53
54/// Carries the error of a nested child repo's write into the parent's
55/// vocabulary: a rejection is lifted into the parent's constraint violation, a
56/// fault passes through.
57pub trait LiftNestedWrite<ParentCv>: Sized {
58 type Out;
59 fn lift_nested(self) -> Self::Out;
60}
61
62impl<ChildCv, ParentCv> LiftNestedWrite<ParentCv> for RepoWriteError<ChildCv>
63where
64 ChildCv: Rejection,
65 ParentCv: Rejection + Lift<ChildCv>,
66 <ParentCv as Lift<ChildCv>>::Unmapped: UnmappedInto<Fatal>,
67{
68 type Out = RepoWriteError<ParentCv>;
69
70 fn lift_nested(self) -> RepoWriteError<ParentCv> {
71 Fail::lift::<ParentCv, errlanes::lanes!(Transient, Fatal)>(self)
72 }
73}
74
75impl<ParentCv> LiftNestedWrite<ParentCv> for RepoFault {
76 type Out = RepoFault;
77
78 fn lift_nested(self) -> RepoFault {
79 let fault: Fault<errlanes::lanes!(Transient, Fatal)> = self;
80 fault
81 }
82}