1#![doc(html_root_url = "https://docs.rs/fera-optional/0.2.0/")]
6
7extern crate num_traits;
76
77use std::fmt;
78use std::marker::PhantomData;
79use std::mem;
80
81use num_traits::bounds::Bounded;
82
83pub type OptionalMax<T> = Optioned<T, MaxNone<T>>;
85
86pub type OptionalMin<T> = Optioned<T, MinNone<T>>;
88
89pub trait Optional<T>: Default + From<T> {
96 fn to_option_ref(&self) -> Option<&T>;
98
99 fn to_option_mut(&mut self) -> Option<&mut T>;
101
102 fn into_option(self) -> Option<T>;
104}
105
106impl<T> Optional<T> for Option<T> {
107 fn to_option_ref(&self) -> Option<&T> {
108 self.as_ref()
109 }
110
111 fn to_option_mut(&mut self) -> Option<&mut T> {
112 self.as_mut()
113 }
114
115 fn into_option(self) -> Option<T> {
116 self
117 }
118}
119
120#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
122pub struct Optioned<T, B: BuildNone<T>> {
123 value: T,
124 _marker: PhantomData<B>,
125}
126
127pub trait BuildNone<T> {
129 fn none() -> T;
130
131 fn desc() -> &'static str;
132}
133
134impl<T: Eq + fmt::Debug, B: BuildNone<T>> fmt::Debug for Optioned<T, B> {
135 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
136 write!(fmt, "Optional{}::{:?}", B::desc(), self.to_option_ref())
137 }
138}
139
140impl<T, B: BuildNone<T>> Default for Optioned<T, B> {
141 fn default() -> Self {
142 Optioned {
143 value: B::none(),
144 _marker: PhantomData,
145 }
146 }
147}
148
149impl<T, B: BuildNone<T>> From<Option<T>> for Optioned<T, B> {
150 fn from(value: Option<T>) -> Self {
151 if let Some(v) = value {
152 Optioned::from(v)
153 } else {
154 Optioned::default()
155 }
156 }
157}
158
159impl<T, B: BuildNone<T>> From<T> for Optioned<T, B> {
160 fn from(value: T) -> Self {
161 Optioned {
162 value: value,
163 _marker: PhantomData,
164 }
165 }
166}
167
168impl<T: Eq, B: BuildNone<T>> Optional<T> for Optioned<T, B> {
169 #[inline(always)]
170 fn to_option_ref(&self) -> Option<&T> {
171 if self.value == B::none() {
172 None
173 } else {
174 Some(&self.value)
175 }
176 }
177
178 #[inline(always)]
179 fn to_option_mut(&mut self) -> Option<&mut T> {
180 if self.value == B::none() {
181 None
182 } else {
183 Some(&mut self.value)
184 }
185 }
186
187 #[inline(always)]
188 fn into_option(self) -> Option<T> {
189 if self.value == B::none() {
190 None
191 } else {
192 Some(self.value)
193 }
194 }
195}
196
197#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
199pub struct MaxNone<T>(PhantomData<T>);
200
201impl<T: Bounded> BuildNone<T> for MaxNone<T> {
202 fn none() -> T {
203 T::max_value()
204 }
205
206 fn desc() -> &'static str {
207 "Max"
208 }
209}
210
211#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
213pub struct MinNone<T>(PhantomData<T>);
214
215impl<T: Bounded> BuildNone<T> for MinNone<T> {
216 fn none() -> T {
217 T::min_value()
218 }
219
220 fn desc() -> &'static str {
221 "Min"
222 }
223}
224
225#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
228pub struct OptionalBool(OptionalMax<u8>);
229
230impl From<bool> for OptionalBool {
231 fn from(value: bool) -> Self {
232 OptionalBool(OptionalMax::from(unsafe {
233 mem::transmute::<bool, u8>(value)
234 }))
235 }
236}
237
238impl fmt::Debug for OptionalBool {
239 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
240 write!(fmt, "OptionalBool::{:?}", self.to_option_ref())
241 }
242}
243
244impl From<Option<bool>> for OptionalBool {
245 fn from(value: Option<bool>) -> Self {
246 if let Some(v) = value {
247 OptionalBool::from(v)
248 } else {
249 OptionalBool::default()
250 }
251 }
252}
253
254impl Optional<bool> for OptionalBool {
255 #[inline(always)]
256 fn to_option_ref(&self) -> Option<&bool> {
257 unsafe { mem::transmute(self.0.to_option_ref()) }
258 }
259
260 #[inline(always)]
261 fn to_option_mut(&mut self) -> Option<&mut bool> {
262 unsafe { mem::transmute(self.0.to_option_mut()) }
263 }
264
265 #[inline(always)]
266 fn into_option(self) -> Option<bool> {
267 self.0.into_option().map(|v| v == 1)
268 }
269}
270
271#[cfg(test)]
272pub trait OptionalTest {
273 type Item: ::std::fmt::Debug + PartialEq;
274 type Sut: Optional<Self::Item>;
275
276 fn values() -> (Self::Item, Self::Item);
277
278 fn sut_default() -> Self::Sut {
279 Self::Sut::default()
280 }
281
282 fn sut_from(value: Self::Item) -> Self::Sut {
283 Self::Sut::from(value)
284 }
285
286 fn to_option_ref() {
287 let (v1, v2) = Self::values();
288 let (x1, x2) = Self::values();
289 assert_eq!(None, Self::sut_default().to_option_ref());
290 assert_eq!(Some(&v1), Self::sut_from(x1).to_option_ref());
291 assert_eq!(Some(&v2), Self::sut_from(x2).to_option_ref());
292 }
293
294 fn to_option_mut() {
295 let (mut v1, mut v2) = Self::values();
296 let (x1, x2) = Self::values();
297 assert_eq!(None, Self::sut_default().to_option_mut());
298 assert_eq!(Some(&mut v1), Self::sut_from(x1).to_option_mut());
299 assert_eq!(Some(&mut v2), Self::sut_from(x2).to_option_mut());
300
301 let (x1, x2) = Self::values();
302 let mut o = Self::sut_from(x1);
303 *o.to_option_mut().unwrap() = x2;
304 assert_eq!(Some(&v2), o.to_option_ref());
305 assert_eq!(Some(&mut v2), o.to_option_mut());
306 assert_eq!(Some(v2), o.into_option());
307 }
308
309 fn into_option() {
310 let (v1, v2) = Self::values();
311 let (x1, x2) = Self::values();
312 assert_eq!(None, Self::sut_default().into_option());
313 assert_eq!(Some(v1), Self::sut_from(x1).into_option());
314 assert_eq!(Some(v2), Self::sut_from(x2).into_option());
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 macro_rules! delegate_tests {
321 ($T: ident, $($names: ident),+) => (
322 $(
323 #[test]
324 fn $names() {
325 $T::$names();
326 }
327 )*
328 )
329 }
330
331 mod optional_bool {
332 use *;
333 struct T;
334
335 impl OptionalTest for T {
336 type Item = bool;
337 type Sut = OptionalBool;
338
339 fn values() -> (Self::Item, Self::Item) {
340 (false, true)
341 }
342 }
343
344 delegate_tests!{T, to_option_ref, to_option_mut, into_option}
345
346 #[test]
347 fn debug() {
348 assert_eq!(
349 "OptionalBool::None",
350 format!("{:?}", OptionalBool::default())
351 );
352 assert_eq!(
353 "OptionalBool::Some(true)",
354 format!("{:?}", OptionalBool::from(true))
355 );
356 assert_eq!(
357 "OptionalBool::Some(false)",
358 format!("{:?}", OptionalBool::from(false))
359 );
360 }
361 }
362
363 mod optioned_u32 {
364 use *;
365 struct T;
366
367 impl OptionalTest for T {
368 type Item = u32;
369 type Sut = OptionalMax<u32>;
370
371 fn values() -> (Self::Item, Self::Item) {
372 (10, 50)
373 }
374 }
375
376 delegate_tests!{T, to_option_ref, to_option_mut, into_option}
377
378 #[test]
379 fn debug() {
380 assert_eq!(
381 "OptionalMax::None",
382 format!("{:?}", OptionalMax::<u32>::default())
383 );
384 assert_eq!(
385 "OptionalMax::Some(10)",
386 format!("{:?}", OptionalMax::from(10u32))
387 );
388
389 assert_eq!(
390 "OptionalMin::None",
391 format!("{:?}", OptionalMin::<u32>::default())
392 );
393 assert_eq!(
394 "OptionalMin::Some(10)",
395 format!("{:?}", OptionalMin::from(10u32))
396 );
397 }
398 }
399}