devela/code/result/chain_hook.rs
1// devela::code::result::traits
2//
3//! Free function chaining traits helpers.
4//
5// Based on the code by George Burton, Unlicense licensed.
6// https://crates.io/crates/apply/0.3.0
7
8/// Allows chaining transformations by passing values through a sequence of functions.
9///
10/// Use `Chain` to thread operations where each function takes ownership or
11/// references the value and returns a new result.
12///
13/// # Examples
14/// ```
15/// use devela::Chain;
16///
17/// let x = 1
18/// .chain(|x| x * 2)
19/// .chain(|x| x + 1)
20/// .chain(|x: i32| x.to_string());
21/// assert_eq![x, 3.to_string()];
22/// ```
23///
24/// ```compile_fail
25/// use devela::Chain;
26///
27/// // We can sort it, but we don't receive the new vec.
28/// let v: Vec<i32> = vec![3, 2, 1, 5].chain_mut(|it| it.sort());
29/// ```
30/// See also the [`Hook`][crate::Hook] trait.
31///
32#[doc = crate::doc_!(vendor: "apply")]
33pub trait Chain<R> {
34 /// Chain a function which takes the parameter by value.
35 #[must_use]
36 fn chain<F>(self, f: F) -> R
37 where
38 F: FnOnce(Self) -> R,
39 Self: Sized,
40 {
41 f(self)
42 }
43
44 /// Chain a function which takes the parameter by shared reference.
45 #[must_use]
46 fn chain_ref<F>(&self, f: F) -> R
47 where
48 F: FnOnce(&Self) -> R,
49 {
50 f(self)
51 }
52
53 /// Chain a function which takes the parameter by exclusive reference.
54 #[must_use]
55 fn chain_mut<F>(&mut self, f: F) -> R
56 where
57 F: FnOnce(&mut Self) -> R,
58 {
59 f(self)
60 }
61}
62impl<T: ?Sized, R> Chain<R> for T {}
63
64/// Allows attaching operations or side effects to a value without breaking its flow.
65///
66/// Use `Hook` when you want to inspect, log, or modify the value in-place without
67/// directly returning a new result from the function.
68///
69/// Useful for when a method doesn't return the receiver
70/// but you want to apply several of them to the object.
71///
72/// It assumes that each function in the chain modifies the value by exclusive
73/// reference and returns the modified value.
74///
75/// # Examples
76/// ```
77/// use devela::Hook;
78///
79/// let v = vec![3, 2, 1, 5]
80/// .hook_mut(|v| v.sort())
81/// .hook_ref(|v| assert_eq![v, &[1, 2, 3, 5]])
82/// .hook_mut(|v| v.push(7));
83/// assert_eq![v, vec![1, 2, 3, 5, 7]];
84/// ```
85/// See also the [`Chain`][crate::Chain] trait.
86///
87#[doc = crate::doc_!(vendor: "apply")]
88pub trait Hook: Sized {
89 /// Applies a function which takes the parameter by shared reference,
90 /// and then returns the (possibly) modified owned value.
91 ///
92 /// Similar to [`chain_ref`], but instead of returning self directly from `f`,
93 /// since it has a different signature, returns it indirectly.
94 ///
95 /// [`chain_ref`]: Chain::chain_ref
96 #[must_use]
97 fn hook_ref<F>(self, f: F) -> Self
98 where
99 F: FnOnce(&Self),
100 {
101 f(&self);
102 self
103 }
104
105 /// Applies a function which takes the parameter by exclusive reference,
106 /// and then returns the (possibly) modified owned value.
107 ///
108 /// Similar to [`chain_mut`], but instead of returning self directly from `f`,
109 /// since it has a different signature, returns it indirectly.
110 ///
111 /// [`chain_mut`]: Chain::chain_mut
112 #[must_use]
113 fn hook_mut<F>(mut self, f: F) -> Self
114 where
115 F: FnOnce(&mut Self),
116 {
117 f(&mut self);
118 self
119 }
120}
121impl<T: Sized> Hook for T {}