@@ -57,6 +57,36 @@ const TRANSACTION_REPL_HISTORY: &'static str = "typedb_console_transaction_repl_
5757const DIAGNOSTICS_REPORTING_URI : & ' static str =
5858 "https://7f0ccb67b03abfccbacd7369d1f4ac6b@o4506315929812992.ingest.sentry.io/4506355433537536" ;
5959
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+
6090struct ConsoleContext {
6191 invocation_dir : PathBuf ,
6292 repl_stack : Vec < Rc < Repl < ConsoleContext > > > ,
@@ -94,7 +124,7 @@ fn main() {
94124 let mut args = Args :: parse ( ) ;
95125 if args. version {
96126 println ! ( "{}" , VERSION ) ;
97- exit ( 0 ) ;
127+ exit ( ExitCode :: Success as i32 ) ;
98128 }
99129 if args. password . is_none ( ) {
100130 args. password = Some ( LineReaderHidden :: new ( ) . readline ( & format ! ( "password for '{}': " , args. username) ) ) ;
@@ -103,13 +133,13 @@ fn main() {
103133 init_diagnostics ( )
104134 }
105135 if !args. tls_disabled && !args. address . starts_with ( "https:" ) {
106- println ! (
136+ eprintln ! (
107137 "\
108138 TLS connections can only be enabled when connecting to HTTPS endpoints, for example using 'https://<ip>:port'. \
109139 Please modify the address, or disable TLS (--tls-disabled). WARNING: this will send passwords over plaintext!\
110140 "
111141 ) ;
112- exit ( 1 ) ;
142+ exit ( ExitCode :: UserInputError as i32 ) ;
113143 }
114144 let runtime = BackgroundRuntime :: new ( ) ;
115145 let tls_root_ca_path = args. tls_root_ca . as_ref ( ) . map ( |value| Path :: new ( value) ) ;
@@ -120,11 +150,11 @@ fn main() {
120150 ) ) {
121151 Ok ( driver) => Arc :: new ( driver) ,
122152 Err ( err) => {
123- println ! ( "Failed to create driver connection to server. {}" , err) ;
153+ eprintln ! ( "Failed to create driver connection to server. {}" , err) ;
124154 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." ) ;
126156 }
127- exit ( 1 ) ;
157+ exit ( ExitCode :: ConnectionError as i32 ) ;
128158 }
129159 } ;
130160
@@ -140,27 +170,34 @@ fn main() {
140170 } ;
141171
142172 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 ) ;
145175 } 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+ }
147179 } 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+ }
149183 } else {
150184 execute_interactive ( & mut context) ;
151185 }
152186}
153187
154- fn execute_scripts ( context : & mut ConsoleContext , files : & [ String ] ) {
188+ fn execute_scripts ( context : & mut ConsoleContext , files : & [ String ] ) -> Result < ( ) , Box < dyn Error > > {
155189 for file_path in files {
156190 let path = context. convert_path ( file_path) ;
157191 if let Ok ( file) = File :: open ( & file_path) {
158192 execute_script ( context, path, io:: BufReader :: new ( file) . lines ( ) )
159193 } 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+ ) ) ) ;
162198 }
163199 }
200+ Ok ( ( ) )
164201}
165202
166203fn execute_script (
@@ -187,13 +224,14 @@ fn execute_script(
187224 context. script_dir = None ;
188225}
189226
190- fn execute_command_list ( context : & mut ConsoleContext , commands : & [ String ] ) {
227+ fn execute_command_list ( context : & mut ConsoleContext , commands : & [ String ] ) -> Result < ( ) , Box < dyn Error > > {
191228 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 ) ) ;
195232 }
196233 }
234+ Ok ( ( ) )
197235}
198236
199237fn execute_interactive ( context : & mut ConsoleContext ) {
@@ -221,7 +259,7 @@ fn execute_interactive(context: &mut ConsoleContext) {
221259 // do nothing
222260 } else {
223261 // this is unexpected... quit
224- exit ( 1 )
262+ exit ( ExitCode :: GeneralError as i32 ) ;
225263 }
226264 }
227265 Err ( err) => {
@@ -236,16 +274,17 @@ fn execute_commands(
236274 mut input : & str ,
237275 coerce_each_command_to_one_line : bool ,
238276 must_log_command : bool ,
239- ) -> Result < ( ) , EmptyError > {
277+ ) -> Result < ( ) , CommandError > {
240278 let mut multiple_commands = None ;
241279 while !context. repl_stack . is_empty ( ) && !input. trim ( ) . is_empty ( ) {
242280 let repl_index = context. repl_stack . len ( ) - 1 ;
243281 let current_repl = context. repl_stack [ repl_index] . clone ( ) ;
244282
245283 input = match current_repl. match_first_command ( input, coerce_each_command_to_one_line) {
246284 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 } ) ;
249288 }
250289 Ok ( Some ( ( command, arguments, next_command_index) ) ) => {
251290 let command_string = & input[ 0 ..next_command_index] ;
@@ -254,19 +293,20 @@ fn execute_commands(
254293 }
255294
256295 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( ) ) ;
258297 }
259298 match command. execute ( context, arguments) {
260299 Ok ( _) => & input[ next_command_index..] ,
261300 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 } ) ;
264304 }
265305 }
266306 }
267307 Err ( err) => {
268- println ! ( "{}" , err) ;
269- return Err ( EmptyError { } ) ;
308+ eprintln ! ( "{}" , err) ;
309+ return Err ( CommandError { message : err . to_string ( ) } ) ;
270310 }
271311 } ;
272312 input = input. trim_start ( ) ;
@@ -432,18 +472,20 @@ fn init_diagnostics() {
432472 ) ) ;
433473}
434474
435- struct EmptyError { }
475+ struct CommandError {
476+ message : String ,
477+ }
436478
437- impl Debug for EmptyError {
479+ impl Debug for CommandError {
438480 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
439481 Display :: fmt ( self , f)
440482 }
441483}
442484
443- impl Display for EmptyError {
485+ impl Display for CommandError {
444486 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
445- write ! ( f, "" )
487+ write ! ( f, "{}" , self . message )
446488 }
447489}
448490
449- impl Error for EmptyError { }
491+ impl Error for CommandError { }
0 commit comments