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