Skip to main content

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}