scosh-core 0.1.0

Portable session state and terminal event core for scosh
Documentation
//! Host-facing events and effect acknowledgements.

use std::fmt;

use crate::{
    input::{InputId, InputRequest, InputResult},
    types::{StateRevision, TerminalDelta, TerminalSnapshot, TerminalState},
};

/// Lifecycle state visible to a host application.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SessionStatus {
    Connecting,
    Live,
    Recovering,
    Suspended,
    Closed,
    Failed,
}

/// Why the host must install a fresh authoritative snapshot.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SnapshotRequiredReason {
    NoBase,
    RevisionGap,
    InvalidDelta,
}

/// A reason-neutral acknowledgement for a host-side terminal effect.
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct CommitReceipt(pub(crate) u64);

impl CommitReceipt {
    /// Returns the opaque value that the host must pass back to acknowledge
    /// this effect.
    pub const fn get(self) -> u64 {
        self.0
    }
}

impl fmt::Debug for CommitReceipt {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("CommitReceipt(<opaque>)")
    }
}

/// Result of acknowledging a reliable host-side effect.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AcknowledgeResult {
    Committed,
    Duplicate,
    RejectedStale,
    RejectedUnknown,
}

/// Effects that must be committed by the host terminal exactly once.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TerminalEffect {
    PrimaryScroll { rows: Vec<crate::PrimaryScrollRow> },
    Screen { alternate: bool },
    InputModes { modes: u16 },
}

/// Events delivered by the portable core.  No wire frame, endpoint, token, or
/// server session identifier crosses this boundary.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SessionEvent {
    Snapshot {
        snapshot: TerminalSnapshot,
    },
    Delta {
        delta: TerminalDelta,
        effects: Vec<TerminalEffect>,
        receipt: Option<CommitReceipt>,
    },
    SnapshotRequired {
        expected: StateRevision,
        received: StateRevision,
        reason: SnapshotRequiredReason,
    },
    StatusChanged(SessionStatus),
    InputQueued {
        request: InputRequest,
    },
    InputCompleted {
        id: InputId,
        outcome: crate::InputOutcome,
    },
    ConsumerStalled,
}

impl SessionEvent {
    /// Return the complete state carried by a snapshot event.
    pub fn snapshot_state(&self) -> Option<&TerminalState> {
        match self {
            Self::Snapshot { snapshot } => Some(&snapshot.state),
            _ => None,
        }
    }

    /// Return the input result carried by an input completion event.
    pub fn input_result(&self) -> Option<InputResult> {
        match self {
            Self::InputCompleted { id, outcome } => Some(InputResult {
                id: *id,
                outcome: *outcome,
            }),
            _ => None,
        }
    }
}