scratch_test_model/aux/json_ctx.rs
1/// Combines an error with a json value that serves as context.
2///
3/// Typically, this json context is the input the function, that produced the error, received.
4#[derive(Debug, PartialEq, Clone, Hash, derive_more::Display)]
5#[display("{inner_error}: {json_ctx}")]
6pub struct JsonCtx<'a, E> {
7 pub(crate) inner_error: E,
8 pub(crate) json_ctx: &'a serde_json::Value,
9}
10
11impl<'a, E> JsonCtx<'a, E> {
12 /// The wrapped error
13 pub fn inner_error(&self) -> &E {
14 &self.inner_error
15 }
16 /// The json value that is related to the error.
17 /// Normally, this will be the input to the function that produced the error.
18 pub fn json_ctx(&self) -> &serde_json::Value {
19 self.json_ctx
20 }
21 /// This consumes the [`JsonCtx`] and returns its parts as tuple
22 /// so that they can be used further.
23 pub fn into_inner(self) -> (E, &'a serde_json::Value) {
24 (self.inner_error, self.json_ctx)
25 }
26 /// Wraps the error in a closure. It doesn't change the context.
27 pub fn map_err<F>(self, m: impl FnOnce(E) -> F) -> JsonCtx<'a, F> {
28 JsonCtx {
29 inner_error: m(self.inner_error),
30 json_ctx: self.json_ctx,
31 }
32 }
33}
34
35#[allow(unused)]
36/// Lifts a function that returns a `Result` given an `&serde_json::Value`
37/// into a function that includes the json value into the error details
38pub fn json_ctx_lift<T, E>(
39 func: impl for<'a> FnOnce(&'a serde_json::Value) -> Result<T, E>,
40) -> impl for<'a> FnOnce(&'a serde_json::Value) -> Result<T, JsonCtx<'a, E>> {
41 |value| {
42 let res = func(value);
43 res.map_err(|err| JsonCtx {
44 inner_error: err,
45 json_ctx: value,
46 })
47 }
48}
49
50/// This is an awesome trick that allows augmenting different computations with
51/// a contextual json value.
52///
53/// This trait is implemented for [`Result`] where it wraps the [`Result::Err`] value with context.
54///
55/// The more interesting implementation is the one for functions (and closures)
56/// that take a reference to [`serde_json::Value`] and produce a result.
57/// It allows _calling_ the method [`WithJsonContextExt::with_ctx`] on an unevaluated
58/// function/method.
59/// This enabled code like `SomeStruct::from_json.with_ctx(value)` where `from_json` is
60/// a function transforming a json value reference into a `Result`.
61pub trait WithJsonContextExt<'a> {
62 type T;
63 type E;
64 fn with_ctx(self, value: &'a serde_json::Value) -> Result<Self::T, JsonCtx<'a, Self::E>>;
65 // fn without_ctx(self, value: &'a serde_json::Value) -> Result<Self::T, Self::E>;
66}
67impl<'a, F, T, E> WithJsonContextExt<'a> for F
68where
69 F: FnOnce(&'a serde_json::Value) -> Result<T, E>,
70{
71 type T = T;
72 type E = E;
73 fn with_ctx(self, value: &'a serde_json::Value) -> Result<Self::T, JsonCtx<'a, Self::E>> {
74 let res = self(value);
75 res.map_err(|err| JsonCtx {
76 inner_error: err,
77 json_ctx: value,
78 })
79 }
80 /*fn without_ctx(self, value: &'a serde_json::Value) -> Result<Self::T, Self::E> {
81 self(value)
82 }*/
83}
84
85impl<'a, T, E> WithJsonContextExt<'a> for Result<T, E> {
86 type T = T;
87 type E = E;
88 fn with_ctx(self, json: &'a serde_json::Value) -> Result<T, JsonCtx<'a, E>> {
89 self.map_err(|err| JsonCtx {
90 inner_error: err,
91 json_ctx: json,
92 })
93 }
94 /*fn without_ctx(self, _value: &'a serde_json::Value) -> Result<Self::T, Self::E> {
95 self
96 }*/
97}