poe_data_tools 1.0.0

A library for working with Path of Exile game data
Documentation
use std::{
    convert::Infallible,
    ops::{ControlFlow, FromResidual, Residual, Try},
};

use anyhow::Context;

pub type VersionedResult<T> = VersionedResult2<T, anyhow::Error>;

/// Result type that optionally carries a version with it. Used in file parsing to track when there
/// are specific file format versions which are failing. Versioned formats often have their version
/// number at the beginning of the file, which can be successfully captured even if parsing fails
/// later on
pub struct VersionedResult2<T, E> {
    pub version: Option<u32>,
    pub inner: Result<T, E>,
}

impl<T> From<VersionedResult2<T, anyhow::Error>> for anyhow::Result<T> {
    fn from(value: VersionedResult2<T, anyhow::Error>) -> Self {
        value
            .inner
            .with_context(|| format!("Fail for version: {:?}", value.version))
    }
}

impl<T> VersionedResult2<T, anyhow::Error> {
    pub fn as_anyhow(self) -> anyhow::Result<T> {
        self.into()
    }
}

impl<T, E> Try for VersionedResult2<T, E> {
    type Output = T;

    type Residual = VersionedResult2<!, E>;

    fn from_output(output: Self::Output) -> Self {
        Self {
            version: None,
            inner: Ok(output),
        }
    }

    fn branch(self) -> ControlFlow<Self::Residual, T> {
        match self.inner {
            Ok(v) => ControlFlow::Continue(v),
            Err(e) => ControlFlow::Break(VersionedResult2 {
                version: self.version,
                inner: Err(e),
            }),
        }
    }
}

/// Helper trait for wrapping results with version info
pub trait VersionedResultExt<T, E> {
    fn with_version(self, version: Option<u32>) -> VersionedResult2<T, E>;
    fn unversioned(self) -> VersionedResult2<T, E>;
}

impl<T, E> VersionedResultExt<T, E> for Result<T, E> {
    fn with_version(self, version: Option<u32>) -> VersionedResult2<T, E> {
        VersionedResult2 {
            version,
            inner: self,
        }
    }

    fn unversioned(self) -> VersionedResult2<T, E> {
        VersionedResult2 {
            version: None,
            inner: self,
        }
    }
}

/// Coerce non-versioned result
impl<T, E> FromResidual<Result<Infallible, E>> for VersionedResult2<T, E> {
    fn from_residual(residual: Result<Infallible, E>) -> Self {
        let Err(e) = residual;

        Self {
            version: None,
            inner: Err(e),
        }
    }
}

/// For propagating versioned results upwards
impl<T, E> FromResidual<VersionedResult2<!, E>> for VersionedResult2<T, E> {
    fn from_residual(residual: VersionedResult2<!, E>) -> Self {
        let Err(e) = residual.inner;

        Self {
            version: residual.version,
            inner: Err(e),
        }
    }
}

// Required due to https://github.com/rust-lang/rust/pull/154451
impl<T, E> Residual<T> for VersionedResult2<!, E> {
    type TryType = VersionedResult2<T, E>;
}