1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
//! Traits for retryable conversions between types.
//!
//! Much like the rust standard library, the traits in this module provide a
//! way to convert from one type to another type, albeit with a focus on using
//! the [`Outcome`](crate::prelude::Outcome). In `outcome`'s case, only two
//! traits are provided, [`AttemptFrom`] and [`AttemptInto`], which mirror
//! [`TryFrom`] and [`TryInto`] respectively.
//!
//! As a library author, you should always prefer implementing [`AttemptFrom`]
//! over [`AttemptInto`], as [`AttemptFrom`] offers greater flexibility and
//! offers an equivalent [`AttemptInto`] implementation for free, thanks to a
//! blanket implementation in the `outcome` crate.
//!
//! # Generic Implementations
//!
//! - [`AttemptFrom`]`<U> for T` implies [`AttemptInto`]`<T> for U`
//!
//! [`AttemptFrom`]: crate::convert::AttemptFrom
//! [`AttemptInto`]: crate::convert::AttemptInto
//! [`TryFrom`]: core::convert::TryFrom
//! [`TryInto`]: core::convert::TryInto
use core::convert::Infallible;
use crate::prelude::{Outcome, Success};
/// Outcome's analogue to [`TryFrom`], and the reciprocal of [`TryInto`].
///
/// This is useful when doing a type conversion that *might* trivially succeed,
/// but also might need special error handling. `AttemptFrom` adds the
/// additional ability to inform the caller that they are free to *retry* the
/// conversion event. This is extremely useful in cases where non-[`Copy`]able
/// types are consumed during the conversion, but users
///
/// 1. Cannot control the error type returned to give useful diagnostics,
/// either to a caller further up the chain OR to logging information
/// 2. May want to try a *different* conversion operation instead (e.g.,
/// trying to treat a string as a filepath *without* having to convert it
/// to the underlying native storage format first)
///
/// # Examples
///
/// ```
/// use outcome::convert::*;
/// use outcome::prelude::*;
///
/// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
/// enum Version { V1, V2 }
///
/// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
/// struct EmptyInput;
///
/// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
/// enum ParseError {
/// InvalidVersion(u8),
/// }
///
/// impl std::fmt::Display for ParseError {
/// fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// match self {
/// Self::InvalidVersion(v) => write!(f, "Expected a valid version, received: {:x?}", v)
/// }
/// }
/// }
///
/// impl<const N: usize> AttemptFrom<&[u8; N]> for Version {
/// type Mistake = EmptyInput;
/// type Failure = ParseError;
///
/// fn attempt_from (value: &[u8; N]) -> Outcome<Self, Self::Mistake, Self::Failure> {
/// match value.get(0) {
/// None => Mistake(EmptyInput),
/// Some(&1) => Success(Version::V1),
/// Some(&2) => Success(Version::V2),
/// Some(&value) => Failure(ParseError::InvalidVersion(value)),
/// }
/// }
/// }
///
/// let empty = Version::attempt_from(&[]);
/// let v1 = Version::attempt_from(&[1u8]);
/// let v2 = Version::attempt_from(&[2u8]);
/// let v3 = Version::attempt_from(&[3u8]);
/// assert_eq!(empty, Mistake(EmptyInput));
/// assert_eq!(v1, Success(Version::V1));
/// assert_eq!(v2, Success(Version::V2));
/// assert_eq!(v3, Failure(ParseError::InvalidVersion(3)));
/// ```
///
/// [`TryFrom`]: core::convert::TryFrom
/// [`TryInto`]: core::convert::TryInto
/// [`Copy`]: core::marker::Copy
pub trait AttemptFrom<T>: Sized {
/// The *retryable* error type
type Mistake;
/// The *failure* error type
type Failure;
/// Performs the conversion
fn attempt_from(value: T) -> Outcome<Self, Self::Mistake, Self::Failure>;
}
/// An attempted conversion that consumes `self`, which may or may not be
/// expensive. Outcome's analogue to [`TryInto`].
///
/// Library writers should *usually* not implement this trait directly, but
/// should prefer implementing the [`AttemptFrom`] trait, which offers more
/// flexibility and provides an equivalent `AttemptInto` implementation for
/// free, thanks to the blanket implementation provided by the `outcome` crate.
///
/// Unlike [`TryInto`], users are free to return a *retryable* error, which
/// *should* return the data consumed (however this cannot be enforced in
/// practice).
///
/// For more information on this, see the documentation for [`Into`].
///
/// # Examples
///
/// The following example uses the same code from [`AttemptFrom`], but calls
/// `attempt_into` on each object instead.
///
/// ```
/// use outcome::convert::*;
/// use outcome::prelude::*;
///
/// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
/// enum Version { V1, V2 }
///
/// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
/// struct EmptyInput;
///
/// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
/// enum ParseError {
/// InvalidVersion(u8),
/// }
///
/// impl std::fmt::Display for ParseError {
/// fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// match self {
/// Self::InvalidVersion(v) => write!(f, "Expected a valid version, received: {:x?}", v)
/// }
/// }
/// }
///
/// impl<const N: usize> AttemptFrom<&[u8; N]> for Version {
/// type Mistake = EmptyInput;
/// type Failure = ParseError;
///
/// fn attempt_from (value: &[u8; N]) -> Outcome<Self, Self::Mistake, Self::Failure> {
/// match value.get(0) {
/// None => Mistake(EmptyInput),
/// Some(&1) => Success(Version::V1),
/// Some(&2) => Success(Version::V2),
/// Some(&value) => Failure(ParseError::InvalidVersion(value)),
/// }
/// }
/// }
///
/// type ParseOutcome = Outcome<Version, EmptyInput, ParseError>;
///
/// let empty: ParseOutcome = (&[]).attempt_into();
/// let v1: ParseOutcome = (&[1u8]).attempt_into();
/// let v2: ParseOutcome = (&[2u8]).attempt_into();
/// let v3: ParseOutcome = (&[3u8]).attempt_into();
/// assert_eq!(empty, Mistake(EmptyInput));
/// assert_eq!(v1, Success(Version::V1));
/// assert_eq!(v2, Success(Version::V2));
/// assert_eq!(v3, Failure(ParseError::InvalidVersion(3)));
/// ```
///
///
/// [`TryInto`]: core::convert::TryInto
/// [`Mutex`]: core::sync::Mutex
/// [`Into`]: core::convert::Into
pub trait AttemptInto<T>: Sized {
/// The type returned in the event of a conversion error where the caller
/// *may* retry the conversion.
type Mistake;
/// The type returned in the event of a conversion error where the caller
/// *may not* retry the conversion.
type Failure;
/// Performs the conversion.
fn attempt_into(self) -> Outcome<T, Self::Mistake, Self::Failure>;
}
/* Blanket Trait Implementations */
impl<T, U> AttemptInto<U> for T
where
U: AttemptFrom<Self>,
{
type Mistake = U::Mistake;
type Failure = U::Failure;
fn attempt_into(self) -> Outcome<U, Self::Mistake, Self::Failure> {
U::attempt_from(self)
}
}
impl<T, U> AttemptFrom<U> for T
where
U: Into<Self>,
{
type Mistake = Infallible;
type Failure = Infallible;
fn attempt_from(value: U) -> Outcome<Self, Self::Mistake, Self::Failure> {
Success(value.into())
}
}
// Reflexive implementation for all [`TryInto`] implementations.
//
// # Notes
//
// If a [`TryInto`] implementation exists because of an [`Into`]
// implementation, the type returned by [`AttemptFrom`] will be an `Outcome<T,
// !, !>`. If the [`unstable` feature](crate#features) is enabled, users can
// then call [`Outcome::into_success`], which will never panic.
//
// ```compile_fail
// # use outcome::prelude::*;
// # use core::convert::Infallible;
// let x: Outcome<u16, Infallible, Infallible> = 1u8.attempt_into();
// assert_eq!(x.into_success(), 1);
// ```
//impl<T, U> AttemptFrom<U> for T
//where
// U: TryInto<Self>,
//{
// type Mistake = Infallible;
// type Failure = <U as TryInto<Self>>::Error;
//
// fn attempt_from(value: U) -> Outcome<Self, Self::Mistake, Self::Failure> {
// match value.try_into() {
// Ok(s) => Success(s),
// Err(f) => Failure(f),
// }
// }
//}