use core::fmt;
use core::ops::Deref;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Linearis<T> {
value: T,
}
impl<T> Linearis<T> {
#[inline]
pub const fn new(value: T) -> Self {
Linearis { value }
}
#[inline]
pub fn consume(self) -> T {
self.value
}
#[inline]
pub fn consume_with<B, F>(self, f: F) -> B
where
F: FnOnce(T) -> B,
{
f(self.value)
}
#[inline]
pub fn fmap_linear<B, F>(self, f: F) -> Linearis<B>
where
F: FnOnce(T) -> B,
{
Linearis::new(f(self.value))
}
#[inline]
pub fn flat_map_linear<B, F>(self, f: F) -> Linearis<B>
where
F: FnOnce(T) -> Linearis<B>,
{
f(self.value)
}
#[inline]
pub fn zip_linear<B>(self, other: Linearis<B>) -> Linearis<(T, B)> {
Linearis::new((self.value, other.value))
}
#[inline]
pub fn ap_linear<B, F>(self, f: Linearis<F>) -> Linearis<B>
where
F: FnOnce(T) -> B,
{
let func = f.consume();
Linearis::new(func(self.value))
}
#[inline]
pub fn replace_linear<B>(self, value: B) -> Linearis<B> {
let _ = self.value;
Linearis::new(value)
}
#[inline]
pub fn void_linear(self) -> Linearis<()> {
let _ = self.value;
Linearis::new(())
}
#[inline]
pub fn peek(&self) -> &T {
&self.value
}
#[inline]
pub fn into_option(self) -> Option<T> {
Some(self.value)
}
#[inline]
pub fn into_result<E>(self) -> Result<T, E> {
Ok(self.value)
}
}
impl<T> Linearis<Option<T>> {
#[inline]
pub fn transpose(self) -> Option<Linearis<T>> {
self.value.map(Linearis::new)
}
}
impl<T, E> Linearis<Result<T, E>> {
#[inline]
pub fn transpose_result(self) -> Result<Linearis<T>, E> {
self.value.map(Linearis::new)
}
}
impl<T: Default> Default for Linearis<T> {
#[inline]
fn default() -> Self {
Linearis::new(T::default())
}
}
impl<T: fmt::Debug> fmt::Debug for Linearis<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Linearis")
.field("value", &self.value)
.finish()
}
}
impl<T: fmt::Display> fmt::Display for Linearis<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Linearis({})", self.value)
}
}
impl<T> From<T> for Linearis<T> {
#[inline]
fn from(value: T) -> Self {
Linearis::new(value)
}
}
impl<T> Deref for Linearis<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
pub trait LinearisExt: Sized {
#[inline]
fn into_linearis(self) -> Linearis<Self> {
Linearis::new(self)
}
}
impl<T> LinearisExt for T {}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
use alloc::string::ToString;
#[test]
fn test_new_and_consume() {
let x = Linearis::new(42);
assert_eq!(x.consume(), 42);
}
#[test]
fn test_consume_with() {
let x = Linearis::new(10);
let result = x.consume_with(|n| n * 2);
assert_eq!(result, 20);
}
#[test]
fn test_fmap_linear() {
let x = Linearis::new(5);
let y = x.fmap_linear(|n| n * 3);
assert_eq!(y.consume(), 15);
}
#[test]
fn test_flat_map_linear() {
let x = Linearis::new(5);
let y = x.flat_map_linear(|n| Linearis::new(n + 10));
assert_eq!(y.consume(), 15);
}
#[test]
fn test_zip_linear() {
let x = Linearis::new(1);
let y = Linearis::new("hello");
let pair = x.zip_linear(y);
assert_eq!(pair.consume(), (1, "hello"));
}
#[test]
fn test_ap_linear() {
let f = Linearis::new(|x: i32| x + 1);
let x = Linearis::new(5);
let result = x.ap_linear(f);
assert_eq!(result.consume(), 6);
}
#[test]
fn test_replace_linear() {
let x = Linearis::new(42);
let y = x.replace_linear("replaced");
assert_eq!(y.consume(), "replaced");
}
#[test]
fn test_void_linear() {
let x = Linearis::new(42);
let voided = x.void_linear();
assert_eq!(voided.consume(), ());
}
#[test]
fn test_peek() {
let x = Linearis::new(42);
assert_eq!(*x.peek(), 42);
assert_eq!(x.consume(), 42);
}
#[test]
fn test_into_option() {
let x = Linearis::new(42);
assert_eq!(x.into_option(), Some(42));
}
#[test]
fn test_into_result() {
let x = Linearis::new(42);
let result: Result<i32, ()> = x.into_result();
assert_eq!(result, Ok(42));
}
#[test]
fn test_transpose_option() {
let x = Linearis::new(Some(42));
let transposed = x.transpose();
assert_eq!(transposed.map(super::Linearis::consume), Some(42));
let none: Linearis<Option<i32>> = Linearis::new(None);
assert!(none.transpose().is_none());
}
#[test]
fn test_transpose_result() {
let x: Linearis<Result<i32, &str>> = Linearis::new(Ok(42));
let transposed = x.transpose_result();
assert_eq!(transposed.map(super::Linearis::consume), Ok(42));
let err: Linearis<Result<i32, &str>> = Linearis::new(Err("error"));
assert_eq!(err.transpose_result(), Err("error"));
}
#[test]
fn test_from_trait() {
let x: Linearis<i32> = 42.into();
assert_eq!(x.consume(), 42);
}
#[test]
fn test_linearis_ext() {
let x = 42.into_linearis();
assert_eq!(x.consume(), 42);
}
#[test]
fn test_default() {
let x: Linearis<i32> = Linearis::default();
assert_eq!(x.consume(), 0);
}
#[test]
fn test_debug() {
let x = Linearis::new(42);
let debug_str = format!("{x:?}");
assert!(debug_str.contains("Linearis"));
assert!(debug_str.contains("42"));
}
#[test]
fn test_display() {
let x = Linearis::new(42);
let display_str = format!("{x}");
assert_eq!(display_str, "Linearis(42)");
}
#[test]
fn test_deref() {
let x = Linearis::new(42);
assert_eq!(*x, 42);
}
#[test]
fn test_eq_ord() {
let x = Linearis::new(42);
let y = Linearis::new(42);
let z = Linearis::new(43);
assert_eq!(x, y);
assert!(x < z);
}
#[test]
fn test_chaining() {
let result = Linearis::new(5i32)
.fmap_linear(|x| x * 2)
.fmap_linear(|x| x + 1)
.flat_map_linear(|x: i32| Linearis::new(x.to_string()))
.consume();
assert_eq!(result, "11");
}
}