1use std::path::PathBuf;
2
3use thiserror::Error;
4
5mod launcher;
6mod manager;
7mod process;
8mod pty;
9mod shell;
10mod stream;
11mod transcript;
12
13#[cfg(not(test))]
14pub(crate) use launcher::login_shell;
15pub(crate) use manager::{TerminalManager, TerminalObservation};
16pub(crate) use shell::ShellMark;
17pub(crate) use stream::ShellObservation;
18pub(crate) use stream::{ReplayPosition, TerminalStream};
19pub(crate) use transcript::Recording;
20pub(crate) use transcript::TranscriptRecorder;
21pub use transcript::TranscriptRequest;
22pub use transcript::TranscriptSize;
23pub use transcript::{MAX_TRANSCRIPT_READ_BYTES, TranscriptError, TranscriptPage, TranscriptRead};
24
25#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
26pub struct TerminalId(u64);
27
28impl TerminalId {
29 #[must_use]
30 pub const fn from_value(value: u64) -> Self {
31 Self(value)
32 }
33
34 #[must_use]
35 pub const fn value(self) -> u64 {
36 self.0
37 }
38}
39
40#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
41pub struct TerminalSize {
42 pub rows: u16,
43 pub columns: u16,
44 pub pixel_width: u16,
45 pub pixel_height: u16,
46}
47
48impl TerminalSize {
49 pub fn validate(self) -> Result<Self, TerminalError> {
53 if self.rows == 0 || self.columns == 0 {
54 return Err(TerminalError::InvalidSize {
55 rows: self.rows,
56 columns: self.columns,
57 });
58 }
59 Ok(self)
60 }
61}
62
63#[derive(Clone, Debug, Eq, PartialEq)]
64pub struct TerminalExit {
65 pub exit_code: u32,
66 pub signal: Option<String>,
67}
68
69#[derive(Clone, Debug, Eq, PartialEq)]
70pub enum TerminalStatus {
71 Running,
72 Exited(TerminalExit),
73 Failed { message: String },
74}
75
76#[derive(Clone, Debug, Eq, PartialEq)]
77pub struct TerminalState {
78 pub terminal_id: TerminalId,
79 pub status: TerminalStatus,
80}
81
82#[derive(Clone, Debug, Eq, PartialEq)]
83pub struct TerminalChunk {
84 pub terminal_id: TerminalId,
85 pub offset: u64,
86 pub bytes: Vec<u8>,
87}
88
89#[derive(Debug, Error)]
90pub enum TerminalError {
91 #[error("terminal output chunk has {chunk_bytes} bytes, exceeding capacity {capacity_bytes}")]
92 ChunkTooLarge {
93 chunk_bytes: usize,
94 capacity_bytes: usize,
95 },
96 #[error("terminal output chunks must not be empty")]
97 EmptyChunk,
98 #[error("terminal size must be nonzero, received {columns} columns by {rows} rows")]
99 InvalidSize { rows: u16, columns: u16 },
100 #[error("terminal byte offset overflowed")]
101 OffsetOverflow,
102 #[error("terminal {terminal_id:?} is not open")]
103 NotOpen { terminal_id: TerminalId },
104 #[error("terminal {terminal_id:?} is no longer running")]
105 NotRunning { terminal_id: TerminalId },
106 #[error("failed to {operation}: {message}")]
107 Pty {
108 operation: &'static str,
109 message: String,
110 },
111 #[error("terminal state lock is poisoned")]
112 Poisoned,
113 #[error("failed to {operation}: {message}")]
114 Thread {
115 operation: &'static str,
116 message: String,
117 },
118 #[error("cannot use {path} as a terminal working directory: {message}")]
119 WorkingDirectory { path: PathBuf, message: String },
120 #[error("terminal output capacity must be greater than zero")]
121 ZeroCapacity,
122 #[error(transparent)]
123 Transcript(#[from] TranscriptError),
124}