gdp_rs/
proven.rs

1//! I define [`Proven`] struct, for representing a proven
2//! proposition about a subject.
3//!
4
5use std::{
6    borrow::Borrow,
7    fmt::{Debug, Display},
8    hash::Hash,
9    marker::PhantomData,
10    ops::Deref,
11    str::FromStr,
12};
13
14use frunk_core::HList;
15
16use crate::{
17    inference_rule::{
18        AuthorizedInferenceRuleGhost, InferenceRule, Operation, PreservingTransformGhost,
19    },
20    predicate::{
21        impl_::all_of::AllOf, AsyncEvaluablePredicate, Predicate, PurePredicate,
22        SyncEvaluablePredicate,
23    },
24};
25
26/// A struct representing a proven proposition about a subject.
27pub struct Proven<S, P> {
28    /// Subject value.
29    subject: S,
30
31    /// Ghost of departed proof.
32    _gdp: PhantomData<P>,
33}
34
35#[cfg(feature = "serde")]
36impl<Sub, P> serde::Serialize for Proven<Sub, P>
37where
38    Sub: serde::Serialize,
39{
40    #[inline]
41    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42    where
43        S: serde::Serializer,
44    {
45        self.subject.serialize(serializer)
46    }
47}
48
49/// An implementation of [`serde::de::Expected`] for indicating
50/// proven value expectation.
51#[cfg(feature = "serde")]
52pub struct ExpectedProven<S, P> {
53    _phantom: PhantomData<fn() -> (S, P)>,
54}
55
56#[cfg(feature = "serde")]
57impl<S, P: Predicate<S>> serde::de::Expected for ExpectedProven<S, P> {
58    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
59        write!(
60            formatter,
61            "a subject value that satisfies {} predicate evaluation",
62            P::label().as_ref()
63        )
64    }
65}
66
67#[cfg(feature = "serde")]
68impl<'de, Sub, P> serde::Deserialize<'de> for Proven<Sub, P>
69where
70    Sub: serde::Deserialize<'de>,
71    P: SyncEvaluablePredicate<Sub>,
72{
73    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74    where
75        D: serde::Deserializer<'de>,
76    {
77        let subject = Sub::deserialize(deserializer)?;
78        Proven::try_new(subject).map_err(|_| {
79            <D::Error as serde::de::Error>::invalid_value(
80                serde::de::Unexpected::Other("predicate evaluation failing subject"),
81                &ExpectedProven::<Sub, P> {
82                    _phantom: PhantomData,
83                },
84            )
85        })
86    }
87}
88
89impl<S, P> Hash for Proven<S, P>
90where
91    S: Hash,
92    P: PurePredicate<S>,
93{
94    #[inline]
95    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
96        self.subject.hash(state);
97    }
98}
99
100impl<S, P> Ord for Proven<S, P>
101where
102    S: Ord,
103    P: PurePredicate<S>,
104{
105    #[inline]
106    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
107        self.subject.cmp(&other.subject)
108    }
109}
110
111impl<S, P> PartialOrd for Proven<S, P>
112where
113    S: PartialOrd,
114    P: PurePredicate<S>,
115{
116    #[inline]
117    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
118        self.subject.partial_cmp(&other.subject)
119    }
120}
121
122impl<S, P> Eq for Proven<S, P>
123where
124    S: Eq,
125    P: PurePredicate<S>,
126{
127}
128
129impl<S, P> PartialEq for Proven<S, P>
130where
131    S: PartialEq,
132    P: PurePredicate<S>,
133{
134    #[inline]
135    fn eq(&self, other: &Self) -> bool {
136        self.subject == other.subject
137    }
138}
139
140impl<S, P> Copy for Proven<S, P>
141where
142    S: Copy,
143    P: PurePredicate<S>,
144{
145}
146
147impl<S, P> Clone for Proven<S, P>
148where
149    S: Clone,
150    P: PurePredicate<S>,
151{
152    #[inline]
153    fn clone(&self) -> Self {
154        Self {
155            subject: self.subject.clone(),
156            _gdp: PhantomData,
157        }
158    }
159}
160
161impl<S, P> Debug for Proven<S, P>
162where
163    S: Debug,
164    P: Predicate<S>,
165{
166    #[inline]
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        write!(f, "Proven<{}>({:?})", P::label(), self.subject)
169    }
170}
171
172impl<S, P> Display for Proven<S, P>
173where
174    S: Display,
175    P: Predicate<S>,
176{
177    #[inline]
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        self.subject.fmt(f)
180    }
181}
182
183impl<S, P> AsRef<S> for Proven<S, P> {
184    #[inline]
185    fn as_ref(&self) -> &S {
186        &self.subject
187    }
188}
189
190impl<S, P> Deref for Proven<S, P> {
191    type Target = S;
192
193    #[inline]
194    fn deref(&self) -> &Self::Target {
195        &self.subject
196    }
197}
198
199impl<S, P> Borrow<S> for Proven<S, P> {
200    #[inline]
201    fn borrow(&self) -> &S {
202        &self.subject
203    }
204}
205
206impl<S, P> Proven<S, P>
207where
208    P: Predicate<S>,
209{
210    /// Convert into proposition subject value.
211    #[inline]
212    pub fn into_subject(self) -> S {
213        self.subject
214    }
215
216    /// Convert subject into other type.
217    pub fn subject_into<T>(self) -> Proven<T, P>
218    where
219        P: Predicate<T>,
220        S: Into<T>,
221    {
222        Proven {
223            subject: self.subject.into(),
224            _gdp: PhantomData,
225        }
226    }
227
228    /// Get new proven proposition with given subject value, with out checks
229    /// .
230    /// # Safety
231    ///
232    /// Callers must ensure that proposition with given subject is true.
233    #[inline]
234    pub const unsafe fn new_unchecked(subject: S) -> Self {
235        Self {
236            subject,
237            _gdp: PhantomData,
238        }
239    }
240
241    /// Apply given preserving transform over proposition subject.
242    pub fn transform_sub<Op: Operation<Arg = S, Result = S> + PreservingTransformGhost<P, S>>(
243        self,
244        op: Op,
245    ) -> Proven<S, P> {
246        Proven {
247            subject: op.call(self.subject),
248            _gdp: PhantomData,
249        }
250    }
251
252    /// Infer new proposition from given proven proposition.
253    #[inline]
254    pub fn infer<R>(self, op: R::SubjectTransform) -> Proven<R::TargetSub, R::TargetPredicate>
255    where
256        R: InferenceRule<SourceSub = S, SourcePredicate = P>,
257        PhantomData<R>: AuthorizedInferenceRuleGhost<R::TargetPredicate, R::TargetSub>,
258    {
259        Proven {
260            subject: op.call(self.subject),
261            _gdp: PhantomData,
262        }
263    }
264
265    /// Infer new proposition from given proven proposition
266    /// using preserving inference rule.
267    #[inline]
268    #[allow(clippy::type_complexity)]
269    pub fn and_infer<R>(
270        self,
271        op: R::SubjectTransform,
272    ) -> Proven<R::TargetSub, AllOf<S, HList!(R::TargetPredicate, P)>>
273    where
274        R: InferenceRule<SourceSub = S, TargetSub = S, SourcePredicate = P>,
275        PhantomData<R::SubjectTransform>: PreservingTransformGhost<P, S>,
276        PhantomData<R>: AuthorizedInferenceRuleGhost<R::TargetPredicate, S>,
277    {
278        Proven {
279            subject: op.call(self.subject),
280            _gdp: PhantomData,
281        }
282    }
283}
284
285impl<S, P> Proven<S, P>
286where
287    P: Predicate<S>,
288{
289    /// Try to create new proven proposition with given subject value.
290    pub fn try_new(subject: S) -> Result<Self, ProvenError<S, P::EvalError>>
291    where
292        P: SyncEvaluablePredicate<S>,
293    {
294        match P::evaluate_for(&subject) {
295            Ok(_) => Ok(Self {
296                subject,
297                _gdp: PhantomData,
298            }),
299            Err(e) => Err(ProvenError { error: e, subject }),
300        }
301    }
302
303    /// Try to create new proven proposition with given subject value.
304    pub async fn try_new_async(subject: S) -> Result<Self, ProvenError<S, P::EvalError>>
305    where
306        P: AsyncEvaluablePredicate<S>,
307    {
308        match P::evaluate_for(&subject).await {
309            Ok(_) => Ok(Self {
310                subject,
311                _gdp: PhantomData,
312            }),
313            Err(e) => Err(ProvenError { error: e, subject }),
314        }
315    }
316
317    /// Try to create proven proposition about subject that can be converted from given value.
318    #[allow(clippy::type_complexity)]
319    pub fn try_new_from<V>(
320        subject: V,
321    ) -> Result<Self, ConvertOrProvenError<<S as TryFrom<V>>::Error, S, P::EvalError>>
322    where
323        S: TryFrom<V> + TryProven<P, Err = ProvenError<S, P::EvalError>> + Sized,
324        P: SyncEvaluablePredicate<S>,
325    {
326        TryInto::<S>::try_into(subject)
327            .map_err(ConvertOrProvenError::ConvertError)?
328            .try_proven()
329            .map_err(ConvertOrProvenError::ProvenError)
330    }
331}
332
333/// A trait for getting converting into proven invariant.
334pub trait TryProven<P>: Sized {
335    /// Type of error.
336    type Err: Debug;
337
338    /// A trait for subjects, to try to create proven propositions with evaluating predicate..
339    fn try_proven(self) -> Result<Proven<Self, P>, Self::Err>;
340}
341
342impl<S, P> TryProven<P> for S
343where
344    S: Sized,
345    P: Predicate<S> + SyncEvaluablePredicate<S>,
346{
347    type Err = ProvenError<S, P::EvalError>;
348
349    #[inline]
350    fn try_proven(self) -> Result<Proven<Self, P>, Self::Err> {
351        Proven::try_new(self)
352    }
353}
354
355impl<S, P> FromStr for Proven<S, P>
356where
357    S: FromStr,
358    P: Predicate<S> + SyncEvaluablePredicate<S>,
359{
360    type Err = ConvertOrProvenError<S::Err, S, P::EvalError>;
361
362    fn from_str(s: &str) -> Result<Self, Self::Err> {
363        S::from_str(s)
364            .map_err(ConvertOrProvenError::ConvertError)?
365            .try_proven()
366            .map_err(ConvertOrProvenError::ProvenError)
367    }
368}
369
370/// A type representing proven predicate errors about a subject.
371#[derive(thiserror::Error)]
372#[error("Error in proving predicate about subject. Error: {error}")]
373pub struct ProvenError<S, PErr> {
374    /// Subject.
375    pub subject: S,
376
377    /// Error.
378    pub error: PErr,
379}
380
381impl<S, PErr: Debug> Debug for ProvenError<S, PErr> {
382    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383        f.debug_struct("ProvenError")
384            .field("error", &self.error)
385            // TODO add subject details?
386            .finish()
387    }
388}
389
390impl<S, PErr> ProvenError<S, PErr> {
391    /// Convert into parts.
392    #[inline]
393    pub fn into_parts(self) -> (S, PErr) {
394        (self.subject, self.error)
395    }
396
397    /// Get the subject.
398    #[inline]
399    pub fn subject(&self) -> &S {
400        &self.subject
401    }
402
403    /// Get the proven error.
404    #[inline]
405    pub fn error(&self) -> &PErr {
406        &self.error
407    }
408}
409/// A sum type representing error for converting + predicate
410/// evaluation operation.
411#[derive(thiserror::Error)]
412pub enum ConvertOrProvenError<C, S, PErr: Display> {
413    /// Error in subject type conversion.
414    #[error("Error in conversion. Error: {0}")]
415    ConvertError(C),
416
417    /// Error in predicate evaluation.
418    #[error("Error in proving the predicate about converted subject. Error: {0}")]
419    ProvenError(ProvenError<S, PErr>),
420}
421
422impl<C, S, PErr: Debug + Display> Debug for ConvertOrProvenError<C, S, PErr> {
423    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424        match self {
425            Self::ConvertError(_) => f.debug_tuple("ConvertError").finish(),
426            Self::ProvenError(e) => f.debug_tuple("ProvenError").field(e).finish(),
427        }
428    }
429}