terrazzo-terminal 0.2.8

A simple web-based terminal emulator built on Terrazzo.
#![cfg(feature = "client")]

use bitflags::bitflags;
use scopeguard::guard;
use terrazzo::prelude::XSignal;

use crate::assets::icons;

#[derive(Clone, Copy, Debug, Default)]
pub struct SyncState {
    pending: Fields,
    loading: Fields,
}

bitflags! {
    #[derive(Clone, Copy, Debug, Default)]
    pub struct Fields: u8 {
        const REMOTE = 1;
        const HOST = 1 << 1;
        const PORT = 1 << 2;
        const DELETE = 1 << 3;
        const ACTIVE = 1 << 4;
    }
}

impl SyncState {
    pub fn status_src(&self) -> icons::Icon {
        self.src(icons::port_forward_synchronized())
    }

    pub fn add_src(&self) -> icons::Icon {
        self.src(icons::add_port_forward())
    }

    fn src(&self, not_pending: icons::Icon) -> icons::Icon {
        if !self.loading.is_empty() {
            icons::port_forward_loading()
        } else if !self.pending.is_empty() {
            icons::port_forward_pending()
        } else {
            not_pending
        }
    }

    pub fn is_deleting(&self) -> bool {
        self.loading.contains(Fields::DELETE)
    }

    pub fn incr_pending(sync_state: XSignal<Self>, field: Fields) {
        sync_state.update(|sync_state| {
            Some(Self {
                pending: sync_state.pending | field,
                loading: sync_state.loading,
            })
        });
    }

    pub fn decr_pending(sync_state: XSignal<Self>, field: Fields) {
        sync_state.update(|sync_state| {
            Some(Self {
                pending: sync_state.pending - field,
                loading: sync_state.loading,
            })
        });
    }

    pub fn incr_loading(sync_state: XSignal<Self>, field: Fields) -> impl Drop {
        Self::incr_impl(
            sync_state,
            move |sync_state| Self {
                pending: sync_state.pending - field,
                loading: sync_state.loading | field,
            },
            move |sync_state| Self {
                pending: sync_state.pending,
                loading: sync_state.loading - field,
            },
        )
    }

    fn incr_impl(
        sync_state: XSignal<Self>,
        incr: impl FnOnce(&Self) -> Self,
        decr: impl FnOnce(&Self) -> Self,
    ) -> impl Drop {
        sync_state.update(|sync_state| Some(incr(sync_state)));
        return guard(sync_state, move |sync_state| {
            sync_state.update(|sync_state| Some(decr(sync_state)));
        });
    }
}