odem-rs-core 0.3.0

Core components of the odem-rs simulation framework
Documentation
//! This module provides a mechanism for compile-time specialization of how
//! `Result` types are processed.
//!
//! In particular, it is used for distinguishing between a standard
//! `Result<T, E>` and a nested `Result<Result<T, E>, V>` where the inner error
//! type `E` can be converted from the outer error type `V`.
//!
//! This capability is primarily intended for use by procedural macros (like the
//! `#[odem_rs::main]` macro) to generate code that behaves differently based on
//! the structure and properties of the `Result` type returned by user code.

use core::fmt::Display;

/// A marker type indicating that a `Result` should be handled by the generic
/// `unwrap` logic.
///
/// This means the `Result` is either not nested or, if nested, its error types
/// do not have a specific `From` conversion path that would qualify it for
/// specialized handling.
pub struct GenericResult;

/// A marker type indicating that a nested `Result` has been identified where
/// the inner error type `E1` can be converted from the outer error type `E2`.
///
/// This allows for specialized error handling.
pub struct ConvertibleResult;

/// Trait for classifying results that should fall back to generic handling.
///
/// The `classify` method returns a [GenericResult] marker.
pub trait Generic {
	/// Classifies `self` as requiring generic result handling.
	#[inline(always)]
	fn classify(&self) -> GenericResult {
		GenericResult
	}
}

/// Trait for classifying results that qualify for specialized (error
/// conversion) handling.
///
/// The `classify` method returns a [ConvertibleResult] marker.
pub trait Special {
	/// Classifies `self` as qualifying for specialized result handling.
	#[inline(always)]
	fn classify(&self) -> ConvertibleResult {
		ConvertibleResult
	}
}

// Implements `Generic` for a reference to any `Result<T, E>`.
//
// This is the fallback implementation. When `(&&some_result).classify()` is
// called, if `some_result` is `Result<T,E>`, the compiler can auto-deref
// `&&some_result` to `&some_result` and match this implementation's `&self`.
impl<T, E: Display> Generic for Result<T, E> {}

// Implements `Special` for a `Result<Result<T, E>, V>` by value,
// where the inner error `E` can be converted from the outer error `V`.
//
// This is the more specific implementation. If `some_result` is of type
// `Result<Result<T, E>, V>` and `E: From<V>`, the compiler prefers this
// implementation over the `Generic` one when `(&&some_result).classify()` is
// called, because it's a direct match for `&self`.
impl<T, E: From<V>, V> Special for &Result<Result<T, E>, V> {}

impl GenericResult {
	/// Unwraps a `Result<T, E>`.
	///
	/// If the result is `Ok(t)`, it returns `t`.
	/// If the result is `Err(e)`, it panics, displaying the error.
	#[inline(always)]
	pub fn unwrap<T, E: Display>(&self, out: Result<T, E>) -> T {
		match out {
			Ok(t) => t,
			Err(e) => panic!("Error: {e}."),
		}
	}
}

impl ConvertibleResult {
	/// Unwraps a nested `Result<Result<T, E>, V>`.
	///
	/// If the outer result is `Ok(inner_result)`, it returns `inner_result`,
	/// which is `Result<T, E>`. If the outer result is `Err(v)`, it converts
	/// `v` into `E` (since `E: From<V>`) and returns `Err(E::from(v))`.
	///
	/// This allows for flattening the nested `Result` while preserving and
	/// converting error types.
	#[inline(always)]
	pub fn unwrap<T, E: From<V>, V>(&self, out: Result<Result<T, E>, V>) -> Result<T, E> {
		out.unwrap_or_else(|err| Err(E::from(err)))
	}
}