@@ -7,6 +7,7 @@ use aoc::all_puzzles;
77use aoc:: utils:: input:: InputType ;
88use std:: error:: Error ;
99use std:: ffi:: CStr ;
10+ use std:: sync:: Once ;
1011
1112const BUFFER_LENGTH : usize = 1024 * 1024 ;
1213
@@ -17,6 +18,8 @@ static mut PART1: [u8; BUFFER_LENGTH] = [0u8; BUFFER_LENGTH];
1718#[ unsafe( no_mangle) ]
1819static mut PART2 : [ u8 ; BUFFER_LENGTH ] = [ 0u8 ; BUFFER_LENGTH ] ;
1920
21+ static ONCE_PANIC_HANDLER : Once = Once :: new ( ) ;
22+
2023#[ unsafe( no_mangle) ]
2124extern "C" fn run_puzzle (
2225 year : u16 ,
@@ -25,17 +28,44 @@ extern "C" fn run_puzzle(
2528 run_part1 : bool ,
2629 run_part2 : bool ,
2730) -> bool {
31+ ONCE_PANIC_HANDLER . call_once ( || {
32+ std:: panic:: set_hook ( Box :: new ( |info| {
33+ let payload = info. payload ( ) ;
34+ let error: & str = if let Some ( s) = payload. downcast_ref :: < & str > ( ) {
35+ s
36+ } else if let Some ( s) = payload. downcast_ref :: < String > ( ) {
37+ s
38+ } else {
39+ return ;
40+ } ;
41+
42+ let location = if let Some ( location) = info. location ( ) {
43+ // Trying to format the location may cause another panic, particularly on allocation
44+ // failure, but it should work on most explicit panics (e.g. "no solution found")
45+ & format ! (
46+ "{}:{}:{}" ,
47+ location. file( ) ,
48+ location. line( ) ,
49+ location. column( )
50+ )
51+ } else {
52+ ""
53+ } ;
54+
55+ write_results ( error, location) ;
56+ } ) ) ;
57+ } ) ;
58+
59+ // Clear result buffers before running the solution, so if it panics, the buffers either contain
60+ // the panic payload and location or are empty.
61+ write_results ( "" , "" ) ;
62+
2863 let ( success, part1, part2) = match run ( year, day, is_example, run_part1, run_part2) {
2964 Ok ( ( part1, part2) ) => ( true , part1, part2) ,
3065 Err ( err) => ( false , err. to_string ( ) , String :: new ( ) ) ,
3166 } ;
3267
33- // SAFETY: No other Rust code accesses these variables or creates references - they're only read
34- // from JS.
35- unsafe {
36- write_string ( ( & raw mut PART1 ) . cast ( ) , & part1) ;
37- write_string ( ( & raw mut PART2 ) . cast ( ) , & part2) ;
38- }
68+ write_results ( & part1, & part2) ;
3969
4070 success
4171}
@@ -75,6 +105,15 @@ fn run(
75105 all_puzzles ! { matcher}
76106}
77107
108+ fn write_results ( part1 : & str , part2 : & str ) {
109+ // SAFETY: No other Rust code accesses these variables or creates references - they're only read
110+ // from JS.
111+ unsafe {
112+ write_string ( ( & raw mut PART1 ) . cast ( ) , part1) ;
113+ write_string ( ( & raw mut PART2 ) . cast ( ) , part2) ;
114+ }
115+ }
116+
78117unsafe fn write_string ( buf : * mut u8 , str : & str ) {
79118 let len = str. len ( ) . min ( BUFFER_LENGTH - 1 ) ;
80119 unsafe {
0 commit comments