@@ -7,14 +7,15 @@ mod output;
77
88use anyhow:: { Context , Result } ;
99use clap:: { ArgMatches , Error , ErrorKind } ;
10- use error:: InvalidTokenError ;
10+ use error:: { InvalidTokenError , SandboxTestError } ;
1111use output:: { CombinedCourseData , ErrorData , Kind , Output , OutputData , OutputResult , Status } ;
1212use serde:: Serialize ;
1313use serde_json:: Value as JsonValue ;
1414use std:: collections:: HashMap ;
1515use std:: env;
1616use std:: fmt:: Debug ;
1717use std:: fs:: { self , File } ;
18+ use std:: io:: Write ;
1819use std:: path:: { Path , PathBuf } ;
1920use tempfile:: NamedTempFile ;
2021use tmc_langs_core:: oauth2:: {
@@ -45,6 +46,7 @@ fn main() {
4546 let causes: Vec < String > = e. chain ( ) . map ( |e| format ! ( "Caused by: {}" , e) ) . collect ( ) ;
4647 let message = error_message_special_casing ( & e) ;
4748 let kind = solve_error_kind ( & e) ;
49+ let sandbox_path = check_sandbox_err ( & e) ;
4850 let error_output = Output :: OutputData ( OutputData {
4951 status : Status :: Finished ,
5052 message : Some ( message) ,
@@ -55,7 +57,7 @@ fn main() {
5557 } ) ,
5658 percent_done : 1.0 ,
5759 } ) ;
58- if let Err ( err) = print_output ( & error_output, pretty) {
60+ if let Err ( err) = print_output_with_file ( & error_output, pretty, sandbox_path ) {
5961 // the above function shouldn't fail ever, but in theory some data could
6062 // have a flawed Serialize implementation, so better safe than sorry
6163 let output = Output :: OutputData :: < ( ) > ( OutputData {
@@ -120,6 +122,20 @@ fn error_message_special_casing(e: &anyhow::Error) -> String {
120122 e. to_string ( )
121123}
122124
125+ /// Goes through the error chain and returns the error output file path if a sandbox test error is found
126+ fn check_sandbox_err ( e : & anyhow:: Error ) -> Option < PathBuf > {
127+ for cause in e. chain ( ) {
128+ // command not found errors are special cased to notify the user that they may need to install additional software
129+ if let Some ( SandboxTestError {
130+ path : Some ( path) , ..
131+ } ) = cause. downcast_ref :: < SandboxTestError > ( )
132+ {
133+ return Some ( path. clone ( ) ) ;
134+ }
135+ }
136+ None
137+ }
138+
123139fn run_app ( matches : ArgMatches , pretty : bool ) -> Result < ( ) > {
124140 // enforces that each branch must return a PrintToken as proof of having printed the output
125141 let _printed: PrintToken = match matches. subcommand ( ) {
@@ -622,7 +638,18 @@ fn run_app(matches: ArgMatches, pretty: bool) -> Result<()> {
622638 "Failed to run tests for exercise at {}" ,
623639 exercise_path. display( )
624640 )
625- } ) ?;
641+ } ) ;
642+
643+ let test_result = if env:: var ( "TMC_SANDBOX" ) . is_ok ( ) {
644+ // in sandbox, wrap error to signal we want to write the output into a file
645+ test_result. map_err ( |e| SandboxTestError {
646+ path : output_path. map ( Path :: to_path_buf) ,
647+ source : e,
648+ } ) ?
649+ } else {
650+ // not in sandbox, just unwrap
651+ test_result?
652+ } ;
626653
627654 if let Some ( output_path) = output_path {
628655 write_result_to_file_as_json ( & test_result, output_path) ?;
@@ -1442,13 +1469,28 @@ fn run_settings(matches: &ArgMatches, pretty: bool) -> Result<PrintToken> {
14421469}
14431470
14441471fn print_output < T : Serialize + Debug > ( output : & Output < T > , pretty : bool ) -> Result < PrintToken > {
1472+ print_output_with_file ( output, pretty, None )
1473+ }
1474+
1475+ fn print_output_with_file < T : Serialize + Debug > (
1476+ output : & Output < T > ,
1477+ pretty : bool ,
1478+ path : Option < PathBuf > ,
1479+ ) -> Result < PrintToken > {
14451480 let result = if pretty {
14461481 serde_json:: to_string_pretty ( & output)
14471482 } else {
14481483 serde_json:: to_string ( & output)
14491484 }
14501485 . with_context ( || format ! ( "Failed to convert {:?} to JSON" , output) ) ?;
14511486 println ! ( "{}" , result) ;
1487+
1488+ if let Some ( path) = path {
1489+ let mut file = File :: create ( & path)
1490+ . with_context ( || format ! ( "Failed to open file at {}" , path. display( ) ) ) ?;
1491+ file. write_all ( result. as_bytes ( ) )
1492+ . with_context ( || format ! ( "Failed to write result to {}" , path. display( ) ) ) ?;
1493+ }
14521494 Ok ( PrintToken )
14531495}
14541496
0 commit comments