finalizable/lib.rs
1#![no_std]
2#![cfg_attr(feature = "try", feature(try_trait_v2))]
3//! This crate provides a type ([`Finalizable`]) for values that can be finalized,
4//! with methods that operate on working values but leave finalized values unchanged.
5
6#[cfg(feature = "try")]
7use core::ops::{ControlFlow, FromResidual, Try};
8
9pub use Finalizable::*;
10
11/// A value that can be a working value or a finalized value.
12/// All operations on a single [`Finalizable<T>`] do not modify a finalized value.
13#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
14pub enum Finalizable<T> {
15 /// A working value.
16 Working(T),
17 /// A finalized value.
18 Finalized(T),
19}
20
21impl<T> Finalizable<T> {
22 /// Create a new finalizable value from a value and a boolean
23 /// that determines if it is a finalized or working value.
24 pub fn new(value: T, finalized: bool) -> Self {
25 match finalized {
26 true => Finalized(value),
27 false => Working(value),
28 }
29 }
30 /// Finalize a value. Returns a finalized version of the value.
31 pub fn finalize(self) -> Self {
32 Finalized(self.get())
33 }
34 /// Get the value, whether working or finalized.
35 pub fn get(self) -> T {
36 match self {
37 Working(x) => x,
38 Finalized(x) => x,
39 }
40 }
41 /// Get the value from a reference to a finalizable value,
42 /// whether working or finalized, as a reference to the underlying value.
43 pub fn get_as_ref(&self) -> &T {
44 self.as_ref().get()
45 }
46 /// Get the value from a mutable reference to a working value
47 /// as a mutable reference. Returns [`None`] if the value is a finalized value.
48 pub fn try_get_mut(&mut self) -> Option<&mut T> {
49 match self {
50 Working(x) => Some(x),
51 Finalized(_) => None,
52 }
53 }
54 /// Override a working value. Does nothing to a finalized value.
55 pub fn set(self, value: T) -> Self {
56 match self {
57 Working(_) => Working(value),
58 a @ Finalized(_) => a,
59 }
60 }
61 /// Check if a value is a working value.
62 pub fn is_working(&self) -> bool {
63 matches!(self, Working(_))
64 }
65 /// Check if a value is a finalized value.
66 pub fn is_finalized(&self) -> bool {
67 matches!(self, Finalized(_))
68 }
69 /// Get the value, but only if it is a working value.
70 /// Returns [`None`] if the value is a finalized value.
71 pub fn working_or_none(self) -> Option<T> {
72 match self {
73 Working(x) => Some(x),
74 Finalized(_) => None,
75 }
76 }
77 /// Get the value, but only if it is a finalized value.
78 /// Returns [`None`] if the value is a working value.
79 pub fn finalized_or_none(self) -> Option<T> {
80 match self {
81 Working(_) => None,
82 Finalized(x) => Some(x),
83 }
84 }
85 /// Get the value, but only if it is a finalized value.
86 /// Returns `default` if the value is a working value.
87 pub fn finalized_or(self, default: T) -> T {
88 match self {
89 Working(_) => default,
90 Finalized(x) => x,
91 }
92 }
93 /// Get the value, but only if it is a finalized value.
94 /// Calls `default` and returns its result if the value is a working value.
95 pub fn finalized_or_else<F: FnOnce(T) -> T>(self, op: F) -> T {
96 match self {
97 Working(x) => op(x),
98 Finalized(x) => x,
99 }
100 }
101 /// Turn a reference to a finalizable value into a finalizable reference.
102 pub fn as_ref(&self) -> Finalizable<&T> {
103 match self {
104 Working(x) => Working(x),
105 Finalized(x) => Finalized(x),
106 }
107 }
108 /// Apply a function to a working value. Does nothing to a finalized value.
109 pub fn map<F: FnOnce(T) -> T>(self, op: F) -> Self {
110 match self {
111 Working(x) => Working(op(x)),
112 a @ Finalized(_) => a,
113 }
114 }
115 /// Apply a function to a working value and finalize it.
116 /// Does nothing to a finalized value.
117 pub fn map_and_finalize<F: FnOnce(T) -> T>(self, op: F) -> Self {
118 self.map(op).finalize()
119 }
120 /// Get a finalized value, panicking with `msg` if the value is a working value.
121 pub fn expect_finalized(self, msg: &str) -> T {
122 match self {
123 Working(x) => x,
124 Finalized(_) => panic!("{}", msg),
125 }
126 }
127 /// Return `fin` if the value is a working value, returning a finalized value unchanged.
128 pub fn and(self, fin: Self) -> Self {
129 match self {
130 Working(_) => fin,
131 a @ Finalized(_) => a,
132 }
133 }
134 /// Call `op` on the value if it is a working value,
135 /// returning a finalized value unchanged.
136 pub fn and_then<F: FnOnce(T) -> Self>(self, op: F) -> Self {
137 match self {
138 Working(x) => op(x),
139 a @ Finalized(_) => a,
140 }
141 }
142 /// Call `op` on the value if it is a working value,
143 /// creating a new finalizable value by using the returned tuple
144 /// as the arguments to [`new`], returning a finalized value unchanged.
145 ///
146 /// [`new`]: Finalizable::new
147 pub fn and_then_new<F: FnOnce(T) -> (T, bool)>(self, op: F) -> Self {
148 self.and_then(|x| {
149 let (value, finalized) = op(x);
150 Finalizable::new(value, finalized)
151 })
152 }
153}
154
155impl<T> Finalizable<&T> {
156 /// Make a copy of a finalizable value by copying the underlying value.
157 pub fn copied(self) -> Finalizable<T>
158 where
159 T: Copy,
160 {
161 match self {
162 Working(x) => Working(*x),
163 Finalized(x) => Finalized(*x),
164 }
165 }
166 /// Make a clone of a finalizable value by cloning the underlying value.
167 pub fn cloned(self) -> Finalizable<T>
168 where
169 T: Clone,
170 {
171 match self {
172 Working(x) => Working(x.clone()),
173 Finalized(x) => Finalized(x.clone()),
174 }
175 }
176}
177
178#[cfg(feature = "try")]
179/// Acts like [`ControlFlow<T, T>`].
180/// Finalized values ([`Finalized`]) break,
181/// working values ([`Working`]) continue.
182impl<T> Try for Finalizable<T> {
183 type Output = T;
184 type Residual = Residual<T>;
185 fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
186 match self {
187 Working(x) => ControlFlow::Continue(x),
188 Finalized(x) => ControlFlow::Break(Residual(x)),
189 }
190 }
191 fn from_output(output: Self::Output) -> Self {
192 Working(output)
193 }
194}
195
196#[cfg(feature = "try")]
197impl<T> FromResidual for Finalizable<T> {
198 fn from_residual(residual: <Self as Try>::Residual) -> Self {
199 Finalized(residual.0)
200 }
201}
202
203#[cfg(feature = "try")]
204/// The residual from applying `?` to a finalized value ([`Finalized`]).
205/// Used in the implementation of [`Try`] for [`Finalizable`].
206pub struct Residual<T>(pub T);