Skip to main content

scosh_core/
lib.rs

1//! Portable session state and terminal event semantics for scosh hosts.
2//!
3//! The crate deliberately contains no transport, process, PTY, or terminal
4//! renderer code.  Desktop and mobile adapters own those boundaries.
5//!
6//! See the repository's [host examples](https://github.com/dyxushuai/scosh/tree/main/examples)
7//! for the event loop used by Apple, Android, and Rust hosts.
8
9pub mod bootstrap;
10pub mod errors;
11pub mod events;
12pub mod input;
13pub mod lifecycle;
14pub mod recovery;
15pub mod types;
16
17mod session;
18
19pub use errors::{ErrorCategory, SdkError};
20pub use events::{
21    AcknowledgeResult, CommitReceipt, SessionEvent, SessionStatus, SnapshotRequiredReason,
22    TerminalEffect,
23};
24pub use input::{InputId, InputOutcome, InputRequest, InputResult};
25pub use session::{Session, SessionOptions};
26pub use types::{
27    Dimensions, PrimaryScrollRow, StateRevision, TerminalCell, TerminalCellStyle, TerminalColor,
28    TerminalCursor, TerminalDelta, TerminalSnapshot, TerminalState, TerminalStateDelta,
29    TerminalStateRow,
30};
31
32use std::fmt;
33
34/// Stable, transport-neutral errors exposed by the portable core.
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub enum CoreError {
37    InvalidDimensions {
38        columns: u16,
39        rows: u16,
40    },
41    InvalidCellCount {
42        expected: usize,
43        actual: usize,
44    },
45    InvalidCursor {
46        column: u16,
47        row: u16,
48    },
49    InvalidRow {
50        expected: u16,
51        actual: usize,
52    },
53    InvalidStateDelta,
54    InvalidUtf8,
55    CellTextTooLarge {
56        size: usize,
57    },
58    StateTooLarge {
59        cells: usize,
60    },
61    TooManyScrollRows {
62        size: usize,
63    },
64    RevisionGap {
65        expected: StateRevision,
66        got: StateRevision,
67    },
68    NoSnapshot,
69    DimensionsMismatch,
70    ConsumerStalled,
71    UnknownReceipt,
72    StaleReceipt,
73    InputTooLarge {
74        size: usize,
75    },
76    NotLive,
77}
78
79impl fmt::Display for CoreError {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::InvalidDimensions { columns, rows } => {
83                write!(f, "invalid terminal dimensions {columns}x{rows}")
84            }
85            Self::InvalidCellCount { expected, actual } => {
86                write!(
87                    f,
88                    "invalid terminal cell count: expected {expected}, got {actual}"
89                )
90            }
91            Self::InvalidCursor { column, row } => write!(f, "invalid cursor {column},{row}"),
92            Self::InvalidRow { expected, actual } => {
93                write!(
94                    f,
95                    "invalid row cell count: expected {expected}, got {actual}"
96                )
97            }
98            Self::InvalidStateDelta => f.write_str("invalid terminal state delta"),
99            Self::InvalidUtf8 => f.write_str("terminal cell is not UTF-8"),
100            Self::CellTextTooLarge { size } => write!(f, "terminal cell is too large: {size}"),
101            Self::StateTooLarge { cells } => {
102                write!(f, "terminal state is too large: {cells} cells")
103            }
104            Self::TooManyScrollRows { size } => write!(f, "too many primary scroll rows: {size}"),
105            Self::RevisionGap { expected, got } => {
106                write!(
107                    f,
108                    "state revision gap: expected {}, got {}",
109                    expected.get(),
110                    got.get()
111                )
112            }
113            Self::NoSnapshot => f.write_str("no terminal snapshot installed"),
114            Self::DimensionsMismatch => {
115                f.write_str("terminal dimensions changed without a snapshot")
116            }
117            Self::ConsumerStalled => f.write_str("host event consumer is stalled"),
118            Self::UnknownReceipt => f.write_str("unknown commit receipt"),
119            Self::StaleReceipt => f.write_str("stale commit receipt"),
120            Self::InputTooLarge { size } => write!(f, "input is too large: {size}"),
121            Self::NotLive => f.write_str("session is not live"),
122        }
123    }
124}
125
126impl std::error::Error for CoreError {}