@@ -57,6 +57,36 @@ const TRANSACTION_REPL_HISTORY: &'static str = "typedb_console_transaction_repl_
57
57
const DIAGNOSTICS_REPORTING_URI : & ' static str =
58
58
"https://7f0ccb67b03abfccbacd7369d1f4ac6b@o4506315929812992.ingest.sentry.io/4506355433537536" ;
59
59
60
+ #[ derive( Debug , Copy , Clone ) ]
61
+ enum ExitCode {
62
+ Success = 0 ,
63
+ GeneralError = 1 ,
64
+ CommandError = 2 ,
65
+ ConnectionError = 3 ,
66
+ UserInputError = 4 ,
67
+ QueryError = 5 ,
68
+ }
69
+
70
+ fn exit_with_error ( err : & ( dyn std:: error:: Error + ' static ) ) -> ! {
71
+ use crate :: repl:: command:: ReplError ;
72
+ if let Some ( repl_err) = err. downcast_ref :: < ReplError > ( ) {
73
+ eprintln ! ( "Error: {}" , repl_err) ;
74
+ exit ( ExitCode :: UserInputError as i32 ) ;
75
+ } else if let Some ( io_err) = err. downcast_ref :: < io:: Error > ( ) {
76
+ eprintln ! ( "I/O Error: {}" , io_err) ;
77
+ exit ( ExitCode :: GeneralError as i32 ) ;
78
+ } else if let Some ( driver_err) = err. downcast_ref :: < typedb_driver:: Error > ( ) {
79
+ eprintln ! ( "TypeDB Error: {}" , driver_err) ;
80
+ exit ( ExitCode :: QueryError as i32 ) ;
81
+ } else if let Some ( command_error) = err. downcast_ref :: < CommandError > ( ) {
82
+ eprintln ! ( "Command Error: {}" , command_error) ;
83
+ exit ( ExitCode :: CommandError as i32 ) ;
84
+ } else {
85
+ eprintln ! ( "Error: {}" , err) ;
86
+ exit ( ExitCode :: GeneralError as i32 ) ;
87
+ }
88
+ }
89
+
60
90
struct ConsoleContext {
61
91
invocation_dir : PathBuf ,
62
92
repl_stack : Vec < Rc < Repl < ConsoleContext > > > ,
@@ -94,7 +124,7 @@ fn main() {
94
124
let mut args = Args :: parse ( ) ;
95
125
if args. version {
96
126
println ! ( "{}" , VERSION ) ;
97
- exit ( 0 ) ;
127
+ exit ( ExitCode :: Success as i32 ) ;
98
128
}
99
129
if args. password . is_none ( ) {
100
130
args. password = Some ( LineReaderHidden :: new ( ) . readline ( & format ! ( "password for '{}': " , args. username) ) ) ;
@@ -103,13 +133,13 @@ fn main() {
103
133
init_diagnostics ( )
104
134
}
105
135
if !args. tls_disabled && !args. address . starts_with ( "https:" ) {
106
- println ! (
136
+ eprintln ! (
107
137
"\
108
138
TLS connections can only be enabled when connecting to HTTPS endpoints, for example using 'https://<ip>:port'. \
109
139
Please modify the address, or disable TLS (--tls-disabled). WARNING: this will send passwords over plaintext!\
110
140
"
111
141
) ;
112
- exit ( 1 ) ;
142
+ exit ( ExitCode :: UserInputError as i32 ) ;
113
143
}
114
144
let runtime = BackgroundRuntime :: new ( ) ;
115
145
let tls_root_ca_path = args. tls_root_ca . as_ref ( ) . map ( |value| Path :: new ( value) ) ;
@@ -120,11 +150,11 @@ fn main() {
120
150
) ) {
121
151
Ok ( driver) => Arc :: new ( driver) ,
122
152
Err ( err) => {
123
- println ! ( "Failed to create driver connection to server. {}" , err) ;
153
+ eprintln ! ( "Failed to create driver connection to server. {}" , err) ;
124
154
if !args. tls_disabled {
125
- println ! ( "Verify that the server is also configured with TLS encryption." ) ;
155
+ eprintln ! ( "Verify that the server is also configured with TLS encryption." ) ;
126
156
}
127
- exit ( 1 ) ;
157
+ exit ( ExitCode :: ConnectionError as i32 ) ;
128
158
}
129
159
} ;
130
160
@@ -140,27 +170,34 @@ fn main() {
140
170
} ;
141
171
142
172
if !args. command . is_empty ( ) && !args. script . is_empty ( ) {
143
- println ! ( "Error: Cannot specify both commands and files" ) ;
144
- exit ( 1 ) ;
173
+ eprintln ! ( "Error: Cannot specify both commands and files" ) ;
174
+ exit ( ExitCode :: UserInputError as i32 ) ;
145
175
} else if !args. command . is_empty ( ) {
146
- execute_command_list ( & mut context, & args. command ) ;
176
+ if let Err ( err) = execute_command_list ( & mut context, & args. command ) {
177
+ exit_with_error ( & * err) ;
178
+ }
147
179
} else if !args. script . is_empty ( ) {
148
- execute_scripts ( & mut context, & args. script ) ;
180
+ if let Err ( err) = execute_scripts ( & mut context, & args. script ) {
181
+ exit_with_error ( & * err) ;
182
+ }
149
183
} else {
150
184
execute_interactive ( & mut context) ;
151
185
}
152
186
}
153
187
154
- fn execute_scripts ( context : & mut ConsoleContext , files : & [ String ] ) {
188
+ fn execute_scripts ( context : & mut ConsoleContext , files : & [ String ] ) -> Result < ( ) , Box < dyn Error > > {
155
189
for file_path in files {
156
190
let path = context. convert_path ( file_path) ;
157
191
if let Ok ( file) = File :: open ( & file_path) {
158
192
execute_script ( context, path, io:: BufReader :: new ( file) . lines ( ) )
159
193
} else {
160
- println ! ( "Error opening file: {}" , path. to_string_lossy( ) ) ;
161
- exit ( 1 ) ;
194
+ return Err ( Box :: new ( io:: Error :: new (
195
+ io:: ErrorKind :: NotFound ,
196
+ format ! ( "Error opening file: {}" , path. to_string_lossy( ) ) ,
197
+ ) ) ) ;
162
198
}
163
199
}
200
+ Ok ( ( ) )
164
201
}
165
202
166
203
fn execute_script (
@@ -187,13 +224,14 @@ fn execute_script(
187
224
context. script_dir = None ;
188
225
}
189
226
190
- fn execute_command_list ( context : & mut ConsoleContext , commands : & [ String ] ) {
227
+ fn execute_command_list ( context : & mut ConsoleContext , commands : & [ String ] ) -> Result < ( ) , Box < dyn Error > > {
191
228
for command in commands {
192
- if let Err ( _ ) = execute_commands ( context, command, true , true ) {
193
- println ! ( "### Stopped executing at command: {}" , command) ;
194
- exit ( 1 ) ;
229
+ if let Err ( err ) = execute_commands ( context, command, true , true ) {
230
+ eprintln ! ( "### Stopped executing at command: {}" , command) ;
231
+ return Err ( Box :: new ( err ) ) ;
195
232
}
196
233
}
234
+ Ok ( ( ) )
197
235
}
198
236
199
237
fn execute_interactive ( context : & mut ConsoleContext ) {
@@ -221,7 +259,7 @@ fn execute_interactive(context: &mut ConsoleContext) {
221
259
// do nothing
222
260
} else {
223
261
// this is unexpected... quit
224
- exit ( 1 )
262
+ exit ( ExitCode :: GeneralError as i32 ) ;
225
263
}
226
264
}
227
265
Err ( err) => {
@@ -236,16 +274,17 @@ fn execute_commands(
236
274
mut input : & str ,
237
275
coerce_each_command_to_one_line : bool ,
238
276
must_log_command : bool ,
239
- ) -> Result < ( ) , EmptyError > {
277
+ ) -> Result < ( ) , CommandError > {
240
278
let mut multiple_commands = None ;
241
279
while !context. repl_stack . is_empty ( ) && !input. trim ( ) . is_empty ( ) {
242
280
let repl_index = context. repl_stack . len ( ) - 1 ;
243
281
let current_repl = context. repl_stack [ repl_index] . clone ( ) ;
244
282
245
283
input = match current_repl. match_first_command ( input, coerce_each_command_to_one_line) {
246
284
Ok ( None ) => {
247
- println ! ( "Unrecognised command: {}" , input) ;
248
- return Err ( EmptyError { } ) ;
285
+ let message = format ! ( "Unrecognised command: {}" , input) ;
286
+ eprintln ! ( "{}" , message) ;
287
+ return Err ( CommandError { message } ) ;
249
288
}
250
289
Ok ( Some ( ( command, arguments, next_command_index) ) ) => {
251
290
let command_string = & input[ 0 ..next_command_index] ;
@@ -254,19 +293,20 @@ fn execute_commands(
254
293
}
255
294
256
295
if must_log_command || multiple_commands. is_some_and ( |b| b) {
257
- println ! ( "{} {}" , "+" . repeat( repl_index + 1 ) , command_string. trim( ) ) ;
296
+ eprintln ! ( "{} {}" , "+" . repeat( repl_index + 1 ) , command_string. trim( ) ) ;
258
297
}
259
298
match command. execute ( context, arguments) {
260
299
Ok ( _) => & input[ next_command_index..] ,
261
300
Err ( err) => {
262
- println ! ( "Error executing command: '{}'\n {}" , command_string. trim( ) , err) ;
263
- return Err ( EmptyError { } ) ;
301
+ let message = format ! ( "Error executing command: '{}'\n {}" , command_string. trim( ) , err) ;
302
+ eprintln ! ( "{}" , message) ;
303
+ return Err ( CommandError { message } ) ;
264
304
}
265
305
}
266
306
}
267
307
Err ( err) => {
268
- println ! ( "{}" , err) ;
269
- return Err ( EmptyError { } ) ;
308
+ eprintln ! ( "{}" , err) ;
309
+ return Err ( CommandError { message : err . to_string ( ) } ) ;
270
310
}
271
311
} ;
272
312
input = input. trim_start ( ) ;
@@ -432,18 +472,20 @@ fn init_diagnostics() {
432
472
) ) ;
433
473
}
434
474
435
- struct EmptyError { }
475
+ struct CommandError {
476
+ message : String ,
477
+ }
436
478
437
- impl Debug for EmptyError {
479
+ impl Debug for CommandError {
438
480
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
439
481
Display :: fmt ( self , f)
440
482
}
441
483
}
442
484
443
- impl Display for EmptyError {
485
+ impl Display for CommandError {
444
486
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
445
- write ! ( f, "" )
487
+ write ! ( f, "{}" , self . message )
446
488
}
447
489
}
448
490
449
- impl Error for EmptyError { }
491
+ impl Error for CommandError { }
0 commit comments