@@ -293,3 +293,82 @@ pub fn fmt_timestamp(timestamp: impl Into<DateTime<Local>>) -> String {
293293 let a: chrono:: DateTime < chrono:: Local > = timestamp. into ( ) ;
294294 format ! ( "{}" , a. format( TIME_FORMAT_HUMANS ) )
295295}
296+
297+ /// Sets up a custom panic handler for user-friendly error reporting.
298+ ///
299+ /// Should be called early in the program startup, ideally before any other operations.
300+ /// In debug builds, uses the default panic handler for detailed debugging output.
301+ /// In release builds, provides a user-friendly error message with reporting instructions.
302+ ///
303+ /// # Example Output
304+ ///
305+ /// ```text
306+ /// Well, this is embarrassing.
307+ ///
308+ /// netpulse had a problem and crashed. This is a bug and should be reported!
309+ ///
310+ /// Technical details:
311+ /// Version: 0.1.0
312+ /// OS: linux x86_64
313+ /// Command: netpulse --check
314+ /// Error: called `Option::unwrap()` on a `None` value
315+ /// Location: src/store.rs:142
316+ ///
317+ /// Please create a new issue at https://github.com/PlexSheep/netpulse/issues
318+ /// with the above technical details and what you were doing when this happened.
319+ /// ```
320+ pub fn setup_panic_handler ( ) {
321+ if !cfg ! ( debug_assertions) {
322+ // Only override in release builds
323+ std:: panic:: set_hook ( Box :: new ( |panic_info| {
324+ let mut message = String :: new ( ) ;
325+ message. push_str ( "\n Well, this is embarrassing.\n \n " ) ;
326+ message. push_str ( & format ! (
327+ "{} had a problem and crashed. This is a bug and should be reported!\n \n " ,
328+ env!( "CARGO_PKG_NAME" )
329+ ) ) ;
330+
331+ message. push_str ( "Technical details:\n " ) ;
332+ message. push_str ( & format ! ( "Version: {}\n " , env!( "CARGO_PKG_VERSION" ) ) ) ;
333+
334+ // Get OS info
335+ #[ cfg( target_os = "linux" ) ]
336+ let os = "linux" ;
337+ #[ cfg( target_os = "macos" ) ]
338+ let os = "macos" ;
339+ #[ cfg( target_os = "windows" ) ]
340+ let os = "windows" ;
341+
342+ message. push_str ( & format ! ( "OS: {} {}\n " , os, std:: env:: consts:: ARCH ) ) ;
343+
344+ // Get command line
345+ let args: Vec < _ > = std:: env:: args ( ) . collect ( ) ;
346+ message. push_str ( & format ! ( "Command: {}\n " , args. join( " " ) ) ) ;
347+
348+ // Extract error message and location
349+ if let Some ( msg) = panic_info. payload ( ) . downcast_ref :: < & str > ( ) {
350+ message. push_str ( & format ! ( "Error: {}\n " , msg) ) ;
351+ } else if let Some ( msg) = panic_info. payload ( ) . downcast_ref :: < String > ( ) {
352+ message. push_str ( & format ! ( "Error: {}\n " , msg) ) ;
353+ }
354+
355+ if let Some ( location) = panic_info. location ( ) {
356+ message. push_str ( & format ! (
357+ "Location: {}:{}\n " ,
358+ location. file( ) ,
359+ location. line( )
360+ ) ) ;
361+ }
362+
363+ message. push_str (
364+ "\n Please create a new issue at https://github.com/PlexSheep/netpulse/issues\n " ,
365+ ) ;
366+ message. push_str (
367+ "with the above technical details and what you were doing when this happened.\n " ,
368+ ) ;
369+
370+ eprintln ! ( "{}" , message) ;
371+ std:: process:: exit ( 1 ) ;
372+ } ) ) ;
373+ }
374+ }
0 commit comments