sigmut 0.0.2

a state management framework designed to be used as a foundation for UI frameworks.
Documentation
use core::fmt;

use crate::{signal::ToSignal, Signal, SignalBuilder, SignalContext};

pub use sigmut_macros::signal_format_dump_raw;
pub use sigmut_macros::signal_format_raw;

pub trait SignalStringBuilder: Sized + 'static {
    fn push_eager(self, f: impl FnOnce(&mut String)) -> Self;
    fn push_lazy(
        self,
        f: impl Fn(&mut String, &mut SignalContext) + 'static,
    ) -> impl SignalStringBuilder;
    fn push_signal<T: ?Sized>(
        self,
        s: Signal<T>,
        f: impl Fn(&mut String, &T) + 'static,
    ) -> impl SignalStringBuilder {
        self.push_lazy(move |buf, sc| f(buf, &*s.borrow(sc)))
    }
    fn push_static(self, s: &'static str) -> impl SignalStringBuilder {
        self.push_lazy(move |buf, _| buf.push_str(s))
    }

    fn with<B>(self, f: impl FnOnce(Self) -> B) -> B {
        f(self)
    }

    fn build(self) -> Signal<str>;
}

pub fn signal_string_builder() -> impl SignalStringBuilder {
    Node {
        buf: String::new(),
        part: NonePart,
    }
}

struct Node<P> {
    buf: String,
    part: P,
}
impl<P: Part> SignalStringBuilder for Node<P> {
    fn push_eager(mut self, f: impl FnOnce(&mut String)) -> Self {
        f(&mut self.buf);
        self
    }

    fn push_lazy(
        self,
        f: impl Fn(&mut String, &mut SignalContext) + 'static,
    ) -> impl SignalStringBuilder {
        Node {
            part: FnPart {
                prev: self.part,
                f,
                buf_end: self.buf.len(),
            },
            buf: self.buf,
        }
    }

    fn build(mut self) -> Signal<str> {
        self.buf.shrink_to_fit();
        SignalBuilder::from_scan(String::new(), move |st, sc| {
            st.clear();
            let offset = self.part.write(&self.buf, st, sc);
            st.push_str(&self.buf[offset..]);
        })
        .map(|st| st.as_str())
        .build()
    }
}

trait Part: 'static {
    fn write(&self, buf: &str, out: &mut String, sc: &mut SignalContext) -> usize;
}

struct NonePart;

impl Part for NonePart {
    fn write(&self, _buf: &str, _out: &mut String, _sc: &mut SignalContext) -> usize {
        0
    }
}

struct FnPart<P, F> {
    prev: P,
    f: F,
    buf_end: usize,
}
impl<P, F> Part for FnPart<P, F>
where
    P: Part + 'static,
    F: Fn(&mut String, &mut SignalContext) + 'static,
{
    fn write(&self, buf: &str, out: &mut String, sc: &mut SignalContext) -> usize {
        let buf_start = self.prev.write(buf, out, sc);
        out.push_str(&buf[buf_start..self.buf_end]);
        (self.f)(out, sc);
        self.buf_end
    }
}

pub struct Helper<'a, S: ?Sized>(pub &'a S);

impl<S: ?Sized + ToSignal> Helper<'_, S> {
    pub fn signal_fmt(
        &self,
        b: impl SignalStringBuilder,
        f: impl Fn(&mut String, FmtRef<S::Value>) -> fmt::Result + 'static,
    ) -> impl SignalStringBuilder {
        b.push_signal(self.0.to_signal(), move |s, v| f(s, FmtRef(v)).unwrap())
    }
}

pub trait HelperForNonSignal {
    type Value: ?Sized;
    fn signal_fmt(
        &self,
        b: impl SignalStringBuilder,
        f: impl Fn(&mut String, FmtRef<Self::Value>) -> fmt::Result + 'static,
    ) -> impl SignalStringBuilder;
}
impl<T: ?Sized> HelperForNonSignal for Helper<'_, T> {
    type Value = T;
    fn signal_fmt(
        &self,
        b: impl SignalStringBuilder,
        f: impl Fn(&mut String, FmtRef<Self::Value>) -> fmt::Result + 'static,
    ) -> impl SignalStringBuilder {
        b.push_eager(|s| f(s, FmtRef(self.0)).unwrap())
    }
}

pub struct FmtRef<'a, T: ?Sized>(&'a T);

impl<'a, T: ?Sized + fmt::Display> fmt::Display for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::Binary> fmt::Binary for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Binary::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::Octal> fmt::Octal for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Octal::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::LowerHex> fmt::LowerHex for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::UpperHex> fmt::UpperHex for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::UpperHex::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::Pointer> fmt::Pointer for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Pointer::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::LowerExp> fmt::LowerExp for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerExp::fmt(self.0, f)
    }
}

impl<'a, T: ?Sized + fmt::UpperExp> fmt::UpperExp for FmtRef<'a, T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::UpperExp::fmt(&self.0, f)
    }
}

pub struct DummyArg;

impl fmt::Display for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::Debug for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::Binary for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::Octal for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::LowerHex for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::UpperHex for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::Pointer for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::LowerExp for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}
impl fmt::UpperExp for DummyArg {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unreachable!()
    }
}