Line data Source code
1 : //! The DashQL shell: a readline-like prompt with SQL highlighting and completion, a VT100
2 : //! terminal renderer, and the execution protocol of queries and dot commands.
3 : //!
4 : //! Queries and commands are pending effects: starting an operation returns
5 : //! [`ShellStatus::Pending`] with an effect envelope (version, type, id, payload), and the host
6 : //! completes or cancels the effect by id, which yields the rendered output.
7 : //!
8 : //! The session does not own its catalog, operations that read it take `&Catalog` (see [`ShellSession`]).
9 : //!
10 : //! Inputs that must be valid UTF-8 are byte slices so that invalid input is reported as an error,
11 : //! and outputs are terminal bytes.
12 :
13 : pub mod prompt_buffer;
14 : mod session;
15 : pub mod unicode;
16 : pub mod vt100;
17 :
18 : pub use prompt_buffer::PromptBuffer;
19 : pub use session::ShellSession;
20 :
21 : /// The status of a shell operation
22 : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
23 : #[repr(u32)]
24 : pub enum ShellStatus {
25 : Ok = 0,
26 : InvalidArgument = 1,
27 : InternalError = 2,
28 : Pending = 3,
29 : StaleEffect = 4,
30 : Busy = 5,
31 : }
32 :
33 : /// The type of an effect
34 : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
35 : #[repr(u32)]
36 : pub enum EffectType {
37 : ExecuteQuery = 1,
38 : ExecuteCommand = 2,
39 : }
40 :
41 : /// How the host finished an effect
42 : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
43 : #[repr(u32)]
44 : pub enum EffectCompletionStatus {
45 : Success = 0,
46 : Error = 1,
47 : Cancelled = 2,
48 : }
49 :
50 : impl EffectCompletionStatus {
51 2 : pub fn from_u32(value: u32) -> Option<Self> {
52 2 : Some(match value {
53 0 : 0 => Self::Success,
54 0 : 1 => Self::Error,
55 1 : 2 => Self::Cancelled,
56 1 : _ => return None,
57 : })
58 2 : }
59 : }
60 :
61 : /// A semantic input key
62 : #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63 : #[repr(u32)]
64 : pub enum PromptInputKey {
65 : Text = 0,
66 : Enter = 1,
67 : ForceSubmit = 2,
68 : Tab = 3,
69 : Backspace = 4,
70 : Delete = 5,
71 : Left = 6,
72 : Right = 7,
73 : HistoryPrevious = 8,
74 : HistoryNext = 9,
75 : Cancel = 10,
76 : Escape = 11,
77 : Up = 12,
78 : Down = 13,
79 : Start = 14,
80 : End = 15,
81 : LineStart = 16,
82 : LineEnd = 17,
83 : WordLeft = 18,
84 : WordRight = 19,
85 : KillLine = 20,
86 : BackwardKillLine = 21,
87 : KillWord = 22,
88 : BackwardKillWord = 23,
89 : Yank = 24,
90 : Transpose = 25,
91 : ClearScreen = 26,
92 : Eof = 27,
93 : Abort = 28,
94 : }
95 :
96 : impl PromptInputKey {
97 : pub const ALL: [PromptInputKey; 29] = [
98 : Self::Text,
99 : Self::Enter,
100 : Self::ForceSubmit,
101 : Self::Tab,
102 : Self::Backspace,
103 : Self::Delete,
104 : Self::Left,
105 : Self::Right,
106 : Self::HistoryPrevious,
107 : Self::HistoryNext,
108 : Self::Cancel,
109 : Self::Escape,
110 : Self::Up,
111 : Self::Down,
112 : Self::Start,
113 : Self::End,
114 : Self::LineStart,
115 : Self::LineEnd,
116 : Self::WordLeft,
117 : Self::WordRight,
118 : Self::KillLine,
119 : Self::BackwardKillLine,
120 : Self::KillWord,
121 : Self::BackwardKillWord,
122 : Self::Yank,
123 : Self::Transpose,
124 : Self::ClearScreen,
125 : Self::Eof,
126 : Self::Abort,
127 : ];
128 :
129 : /// The key of a numeric value, None for values past `ABORT`
130 2 : pub fn from_u32(value: u32) -> Option<Self> {
131 2 : Self::ALL.get(value as usize).copied()
132 2 : }
133 : }
134 :
135 : /// What the host should do after an input
136 : #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
137 : #[repr(u32)]
138 : pub enum PromptInputAction {
139 : #[default]
140 : None = 0,
141 : Submit = 1,
142 : Complete = 2,
143 : Exit = 3,
144 : }
145 :
146 : /// The result of an operation: rendered output, an effect envelope or an error message
147 : #[derive(Clone, Debug, PartialEq, Eq)]
148 : pub struct ShellOperation {
149 : /// The status of the operation
150 : pub status: ShellStatus,
151 : /// The rendered output, the encoded effect envelope or the error message
152 : pub data: Vec<u8>,
153 : }
154 :
155 : impl ShellOperation {
156 432 : pub fn new(status: ShellStatus, data: impl Into<Vec<u8>>) -> Self {
157 432 : Self { status, data: data.into() }
158 432 : }
159 :
160 394 : pub fn ok(data: impl Into<Vec<u8>>) -> Self {
161 394 : Self::new(ShellStatus::Ok, data)
162 394 : }
163 : }
164 :
165 : /// Terminal output with the action of the input
166 : #[derive(Clone, Debug, PartialEq, Eq)]
167 : pub struct TerminalOutput {
168 : /// The status of the input
169 : pub status: ShellStatus,
170 : /// What the host should do after the input
171 : pub action: PromptInputAction,
172 : /// The terminal output with VT100 escape sequences, or the error message
173 : pub data: Vec<u8>,
174 : }
175 :
176 : /// A completion candidate of the prompt
177 : #[derive(Clone, Debug, Default, PartialEq, Eq)]
178 : pub struct CompletionCandidate {
179 : /// The text shown in the completion list
180 : pub display_text: String,
181 : /// The text that replaces the target, quoted if needed and with `()` for functions
182 : pub completion_text: String,
183 : /// The keywords that usually follow the completion, shown as hint, empty if none
184 : pub continuation_text: String,
185 : /// Whether this candidate only preserves the token already under the cursor
186 : pub is_identity: bool,
187 : /// The qualified names of the candidate objects that are shown as variants
188 : pub qualification_texts: Vec<String>,
189 : /// The cursor offset within the completion text after applying it
190 : pub completion_cursor_offset: u32,
191 : /// The byte offset of the replaced text in the prompt
192 : pub target_offset: u32,
193 : /// The byte length of the replaced text in the prompt
194 : pub target_length: u32,
195 : /// The byte offset of the text that a qualified name replaces
196 : pub qualification_target_offset: u32,
197 : /// The byte length of the text that a qualified name replaces
198 : pub qualification_target_length: u32,
199 : }
200 :
201 : /// The completion list that the host shows over the terminal
202 : #[derive(Clone, Copy, Debug, PartialEq, Eq)]
203 : pub struct TerminalCompletionList {
204 : /// Changes when the candidates change, the host only reads the buffer of the list then
205 : pub revision: u64,
206 : /// The index of the selected candidate
207 : pub selection: u32,
208 : /// The index of the selected catalog object of the selected candidate
209 : pub variant: u32,
210 : /// The terminal column of the start of the replaced text
211 : pub anchor_column: u32,
212 : }
213 :
214 : /// The state of the prompt after an edit
215 : #[derive(Clone, Debug, PartialEq, Eq)]
216 : pub struct PromptSnapshot {
217 : /// The status of the edit
218 : pub status: ShellStatus,
219 : /// The revision of the prompt text
220 : pub revision: u64,
221 : /// The byte offset of the cursor in the text
222 : pub cursor_byte_offset: u32,
223 : /// The prompt text
224 : pub text: String,
225 : /// The error message, empty on success
226 : pub message: String,
227 : /// What the host should do after the edit
228 : pub action: PromptInputAction,
229 : }
230 :
231 : /// The version of the effect envelope
232 : pub const EFFECT_ENVELOPE_VERSION: u32 = 1;
233 : /// The size of the effect envelope header: version (u32), type (u32), id (u64), all little-endian
234 : pub const EFFECT_ENVELOPE_HEADER_SIZE: usize = 16;
|