@@ -452,6 +452,10 @@ macro_rules! compatibility_check {
452452 $( $feature) *
453453 ] ;
454454 }
455+
456+ pub fn compatibility_check_once( ) {
457+ // no-op when exporting
458+ }
455459 } ;
456460}
457461
@@ -539,199 +543,198 @@ macro_rules! compatibility_check {
539543 len
540544 }
541545
542- // this one is _actually_ meant to exist once per shared object
543- static COMPATIBILITY_CHECK_ONCE : std:: sync:: Once = std:: sync:: Once :: new( ) ;
544-
545546 pub fn compatibility_check_once( ) {
546- COMPATIBILITY_CHECK_ONCE . call_once( || {
547- check_compatibility( ) ;
548- } ) ;
549- }
550-
551- pub fn check_compatibility( ) {
552- let imported: & [ ( & str , & str ) ] = & [
553- ( "rustc-version" , $crate:: RUBICON_RUSTC_VERSION ) ,
554- ( "target-triple" , $crate:: RUBICON_TARGET_TRIPLE ) ,
555- $( $feature) *
556- ] ;
557- let exported = unsafe { COMPATIBILITY_INFO } ;
558-
559- let missing: Vec <_> = imported. iter( ) . filter( |& item| !exported. contains( item) ) . collect( ) ;
560- let extra: Vec <_> = exported. iter( ) . filter( |& item| !imported. contains( item) ) . collect( ) ;
561-
562- if missing. is_empty( ) && extra. is_empty( ) {
563- // all good
564- return ;
565- }
566-
567- let so_name = get_shared_object_name( ) . unwrap_or( "unknown_so" . to_string( ) ) ;
568- // get only the last bit of the path
569- let so_name = so_name. rsplit( '/' ) . next( ) . unwrap_or( "unknown_so" ) ;
570-
571- let exe_name = std:: env:: current_exe( ) . map( |p| p. file_name( ) . unwrap( ) . to_string_lossy( ) . to_string( ) ) . unwrap_or_else( |_| "unknown_exe" . to_string( ) ) ;
547+ fn check_compatibility( ) {
548+ let imported: & [ ( & str , & str ) ] = & [
549+ ( "rustc-version" , $crate:: RUBICON_RUSTC_VERSION ) ,
550+ ( "target-triple" , $crate:: RUBICON_TARGET_TRIPLE ) ,
551+ $( $feature) *
552+ ] ;
553+ let exported = unsafe { COMPATIBILITY_INFO } ;
554+
555+ let missing: Vec <_> = imported. iter( ) . filter( |& item| !exported. contains( item) ) . collect( ) ;
556+ let extra: Vec <_> = exported. iter( ) . filter( |& item| !imported. contains( item) ) . collect( ) ;
557+
558+ if missing. is_empty( ) && extra. is_empty( ) {
559+ // all good
560+ return ;
561+ }
572562
573- let mut error_message = String :: new ( ) ;
574- error_message . push_str ( " \n \x1b [31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \x1b [0m \n " ) ;
575- error_message . push_str ( & format! ( " 💀 Feature mismatch for crate \x1b [31m{} \x1b [0m \n \n " , env! ( "CARGO_PKG_NAME" ) ) ) ;
563+ let so_name = get_shared_object_name ( ) . unwrap_or ( "unknown_so" . to_string ( ) ) ;
564+ // get only the last bit of the path
565+ let so_name = so_name . rsplit ( '/' ) . next ( ) . unwrap_or ( "unknown_so" ) ;
576566
577- error_message . push_str ( & format! ( "{} has an incompatible configuration for {}. \n \n " , blue ( so_name ) , red ( env! ( "CARGO_PKG_NAME" ) ) ) ) ;
567+ let exe_name = std :: env :: current_exe ( ) . map ( |p| p . file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_string ( ) ) . unwrap_or_else ( |_| "unknown_exe" . to_string ( ) ) ;
578568
579- // Compute max lengths for alignment
580- let max_exported_len = exported. iter( ) . map( |( k, v) | format!( "{}={}" , k, v) . len( ) ) . max( ) . unwrap_or( 0 ) ;
581- let max_ref_len = imported. iter( ) . map( |( k, v) | format!( "{}={}" , k, v) . len( ) ) . max( ) . unwrap_or( 0 ) ;
582- let column_width = max_exported_len. max( max_ref_len) ;
569+ let mut error_message = String :: new( ) ;
570+ error_message. push_str( "\n \x1b [31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b [0m\n " ) ;
571+ error_message. push_str( & format!( " 💀 Feature mismatch for crate \x1b [31m{}\x1b [0m\n \n " , env!( "CARGO_PKG_NAME" ) ) ) ;
583572
584- // Gather all unique keys
585- let mut all_keys: Vec <& str > = Vec :: new( ) ;
586- for ( key, _) in exported. iter( ) {
587- if !all_keys. contains( key) {
588- all_keys. push( key) ;
589- }
590- }
591- for ( key, _) in imported. iter( ) {
592- if !all_keys. contains( key) {
593- all_keys. push( key) ;
594- }
595- }
573+ error_message. push_str( & format!( "{} has an incompatible configuration for {}.\n \n " , blue( so_name) , red( env!( "CARGO_PKG_NAME" ) ) ) ) ;
596574
597- struct Grid {
598- rows : Vec < Vec < String >> ,
599- column_widths : Vec < usize > ,
600- }
575+ // Compute max lengths for alignment
576+ let max_exported_len = exported . iter ( ) . map ( | ( k , v ) | format! ( "{}={}" , k , v ) . len ( ) ) . max ( ) . unwrap_or ( 0 ) ;
577+ let max_ref_len = imported . iter ( ) . map ( | ( k , v ) | format! ( "{}={}" , k , v ) . len ( ) ) . max ( ) . unwrap_or ( 0 ) ;
578+ let column_width = max_exported_len . max ( max_ref_len ) ;
601579
602- impl Grid {
603- fn new ( ) -> Self {
604- Grid {
605- rows : Vec :: new ( ) ,
606- column_widths : Vec :: new ( ) ,
580+ // Gather all unique keys
581+ let mut all_keys : Vec < & str > = Vec :: new ( ) ;
582+ for ( key , _ ) in exported . iter ( ) {
583+ if !all_keys . contains ( key ) {
584+ all_keys . push ( key ) ;
607585 }
608586 }
609-
610- fn add_row( & mut self , row: Vec <String >) {
611- if self . column_widths. len( ) < row. len( ) {
612- self . column_widths. resize( row. len( ) , 0 ) ;
613- }
614- for ( i, cell) in row. iter( ) . enumerate( ) {
615- self . column_widths[ i] = self . column_widths[ i] . max( visible_len( cell) ) ;
587+ for ( key, _) in imported. iter( ) {
588+ if !all_keys. contains( key) {
589+ all_keys. push( key) ;
616590 }
617- self . rows. push( row) ;
618591 }
619592
620- fn write_to( & self , out: & mut String ) {
621- let total_width: usize = self . column_widths. iter( ) . sum:: <usize >( ) + self . column_widths. len( ) * 3 - 1 ;
622-
623- // Top border
624- out. push_str( & format!( "┌{}┐\n " , "─" . repeat( total_width) ) ) ;
593+ struct Grid {
594+ rows: Vec <Vec <String >>,
595+ column_widths: Vec <usize >,
596+ }
625597
626- for ( i, row) in self . rows. iter( ) . enumerate( ) {
627- if i == 1 {
628- // Separator after header
629- out. push_str( & format!( "╞{}╡\n " , "═" . repeat( total_width) ) ) ;
598+ impl Grid {
599+ fn new( ) -> Self {
600+ Grid {
601+ rows: Vec :: new( ) ,
602+ column_widths: Vec :: new( ) ,
630603 }
604+ }
631605
632- for ( j, cell) in row. iter( ) . enumerate( ) {
633- out. push_str( "│ " ) ;
634- out. push_str( cell) ;
635- out. push_str( & " " . repeat( self . column_widths[ j] - visible_len( cell) ) ) ;
636- out. push_str( " " ) ;
606+ fn add_row( & mut self , row: Vec <String >) {
607+ if self . column_widths. len( ) < row. len( ) {
608+ self . column_widths. resize( row. len( ) , 0 ) ;
609+ }
610+ for ( i, cell) in row. iter( ) . enumerate( ) {
611+ self . column_widths[ i] = self . column_widths[ i] . max( visible_len( cell) ) ;
637612 }
638- out . push_str ( "│ \n " ) ;
613+ self . rows . push ( row ) ;
639614 }
640615
641- // Bottom border
642- out. push_str( & format!( "└{}┘\n " , "─" . repeat( total_width) ) ) ;
643- }
644- }
645-
646- let mut grid = Grid :: new( ) ;
616+ fn write_to( & self , out: & mut String ) {
617+ let total_width: usize = self . column_widths. iter( ) . sum:: <usize >( ) + self . column_widths. len( ) * 3 - 1 ;
647618
648- // Add header
649- grid . add_row ( vec! [ "Key" . to_string ( ) , format! ( "Binary {}" , blue ( & exe_name ) ) , format!( "Module {} " , blue ( so_name ) ) ] ) ;
619+ // Top border
620+ out . push_str ( & format!( "┌{}┐ \n " , "─" . repeat ( total_width ) ) ) ;
650621
651- for key in all_keys. iter( ) {
652- let exported_value = exported. iter( ) . find( |& ( k, _) | k == key) . map( |( _, v) | v) ;
653- let imported_value = imported. iter( ) . find( |& ( k, _) | k == key) . map( |( _, v) | v) ;
654-
655- let key_column = colored( AnsiColor :: GREY , key) . to_string( ) ;
656- let binary_column = format_column( exported_value. as_deref( ) . copied( ) , imported_value. as_deref( ) . copied( ) , AnsiColor :: GREEN ) ;
657- let module_column = format_column( imported_value. as_deref( ) . copied( ) , exported_value. as_deref( ) . copied( ) , AnsiColor :: RED ) ;
622+ for ( i, row) in self . rows. iter( ) . enumerate( ) {
623+ if i == 1 {
624+ // Separator after header
625+ out. push_str( & format!( "╞{}╡\n " , "═" . repeat( total_width) ) ) ;
626+ }
658627
659- fn format_column( primary: Option <& str >, secondary: Option <& str >, highlight_color: AnsiColor ) -> String {
660- match primary {
661- Some ( value) => {
662- if secondary. map_or( false , |v| v == value) {
663- colored( AnsiColor :: GREY , value) . to_string( )
664- } else {
665- colored( highlight_color, value) . to_string( )
628+ for ( j, cell) in row. iter( ) . enumerate( ) {
629+ out. push_str( "│ " ) ;
630+ out. push_str( cell) ;
631+ out. push_str( & " " . repeat( self . column_widths[ j] - visible_len( cell) ) ) ;
632+ out. push_str( " " ) ;
666633 }
667- } ,
668- None => colored( AnsiColor :: RED , "∅" ) . to_string( ) ,
634+ out. push_str( "│\n " ) ;
635+ }
636+
637+ // Bottom border
638+ out. push_str( & format!( "└{}┘\n " , "─" . repeat( total_width) ) ) ;
669639 }
670640 }
671641
672- grid. add_row( vec![ key_column, binary_column, module_column] ) ;
673- }
642+ let mut grid = Grid :: new( ) ;
643+
644+ // Add header
645+ grid. add_row( vec![ "Key" . to_string( ) , format!( "Binary {}" , blue( & exe_name) ) , format!( "Module {}" , blue( so_name) ) ] ) ;
646+
647+ for key in all_keys. iter( ) {
648+ let exported_value = exported. iter( ) . find( |& ( k, _) | k == key) . map( |( _, v) | v) ;
649+ let imported_value = imported. iter( ) . find( |& ( k, _) | k == key) . map( |( _, v) | v) ;
650+
651+ let key_column = colored( AnsiColor :: GREY , key) . to_string( ) ;
652+ let binary_column = format_column( exported_value. as_deref( ) . copied( ) , imported_value. as_deref( ) . copied( ) , AnsiColor :: GREEN ) ;
653+ let module_column = format_column( imported_value. as_deref( ) . copied( ) , exported_value. as_deref( ) . copied( ) , AnsiColor :: RED ) ;
654+
655+ fn format_column( primary: Option <& str >, secondary: Option <& str >, highlight_color: AnsiColor ) -> String {
656+ match primary {
657+ Some ( value) => {
658+ if secondary. map_or( false , |v| v == value) {
659+ colored( AnsiColor :: GREY , value) . to_string( )
660+ } else {
661+ colored( highlight_color, value) . to_string( )
662+ }
663+ } ,
664+ None => colored( AnsiColor :: RED , "∅" ) . to_string( ) ,
665+ }
666+ }
674667
675- grid. write_to( & mut error_message) ;
668+ grid. add_row( vec![ key_column, binary_column, module_column] ) ;
669+ }
676670
677- struct MessageBox {
678- lines: Vec <String >,
679- max_width: usize ,
680- }
671+ grid. write_to( & mut error_message) ;
681672
682- impl MessageBox {
683- fn new( ) -> Self {
684- MessageBox {
685- lines: Vec :: new( ) ,
686- max_width: 0 ,
687- }
673+ struct MessageBox {
674+ lines: Vec <String >,
675+ max_width: usize ,
688676 }
689677
690- fn add_line( & mut self , line: String ) {
691- self . max_width = self . max_width. max( visible_len( & line) ) ;
692- self . lines. push( line) ;
693- }
678+ impl MessageBox {
679+ fn new( ) -> Self {
680+ MessageBox {
681+ lines: Vec :: new( ) ,
682+ max_width: 0 ,
683+ }
684+ }
694685
695- fn add_empty_line( & mut self ) {
696- self . lines. push( String :: new( ) ) ;
697- }
686+ fn add_line( & mut self , line: String ) {
687+ self . max_width = self . max_width. max( visible_len( & line) ) ;
688+ self . lines. push( line) ;
689+ }
690+
691+ fn add_empty_line( & mut self ) {
692+ self . lines. push( String :: new( ) ) ;
693+ }
698694
699- fn write_to( & self , out: & mut String ) {
700- let box_width = self . max_width + 4 ;
695+ fn write_to( & self , out: & mut String ) {
696+ let box_width = self . max_width + 4 ;
701697
702- out. push_str( "\n " ) ;
703- out. push_str( & format!( "┌{}┐\n " , "─" . repeat( box_width - 2 ) ) ) ;
698+ out. push_str( "\n " ) ;
699+ out. push_str( & format!( "┌{}┐\n " , "─" . repeat( box_width - 2 ) ) ) ;
704700
705- for line in & self . lines {
706- if line. is_empty( ) {
707- out. push_str( & format!( "│{}│\n " , " " . repeat( box_width - 2 ) ) ) ;
708- } else {
709- let visible_line_len = visible_len( line) ;
710- let padding = " " . repeat( box_width - 4 - visible_line_len) ;
711- out. push_str( & format!( "│ {}{} │\n " , line, padding) ) ;
701+ for line in & self . lines {
702+ if line. is_empty( ) {
703+ out. push_str( & format!( "│{}│\n " , " " . repeat( box_width - 2 ) ) ) ;
704+ } else {
705+ let visible_line_len = visible_len( line) ;
706+ let padding = " " . repeat( box_width - 4 - visible_line_len) ;
707+ out. push_str( & format!( "│ {}{} │\n " , line, padding) ) ;
708+ }
712709 }
713- }
714710
715- out. push_str( & format!( "└{}┘" , "─" . repeat( box_width - 2 ) ) ) ;
711+ out. push_str( & format!( "└{}┘" , "─" . repeat( box_width - 2 ) ) ) ;
712+ }
716713 }
717- }
718714
719- error_message. push_str( "\n Different feature sets may result in different struct layouts, which\n " ) ;
720- error_message. push_str( "would lead to memory corruption. Instead, we're going to panic now.\n \n " ) ;
715+ error_message. push_str( "\n Different feature sets may result in different struct layouts, which\n " ) ;
716+ error_message. push_str( "would lead to memory corruption. Instead, we're going to panic now.\n \n " ) ;
721717
722- error_message. push_str( "More info: \x1b [4m\x1b [34mhttps://crates.io/crates/rubicon\x1b [0m\n " ) ;
718+ error_message. push_str( "More info: \x1b [4m\x1b [34mhttps://crates.io/crates/rubicon\x1b [0m\n " ) ;
723719
724- let mut message_box = MessageBox :: new( ) ;
725- message_box. add_line( format!( "To fix this issue, {} needs to enable" , blue( so_name) ) ) ;
726- message_box. add_line( format!( "the same cargo features as {} for crate {}." , blue( & exe_name) , red( env!( "CARGO_PKG_NAME" ) ) ) ) ;
727- message_box. add_empty_line( ) ;
728- message_box. add_line( "\x1b [34mHINT:\x1b [0m" . to_string( ) ) ;
729- message_box. add_line( format!( "Run `cargo tree -i {} -e features` from both." , red( env!( "CARGO_PKG_NAME" ) ) ) ) ;
720+ let mut message_box = MessageBox :: new( ) ;
721+ message_box. add_line( format!( "To fix this issue, {} needs to enable" , blue( so_name) ) ) ;
722+ message_box. add_line( format!( "the same cargo features as {} for crate {}." , blue( & exe_name) , red( env!( "CARGO_PKG_NAME" ) ) ) ) ;
723+ message_box. add_empty_line( ) ;
724+ message_box. add_line( "\x1b [34mHINT:\x1b [0m" . to_string( ) ) ;
725+ message_box. add_line( format!( "Run `cargo tree -i {} -e features` from both." , red( env!( "CARGO_PKG_NAME" ) ) ) ) ;
730726
731- message_box. write_to( & mut error_message) ;
732- error_message. push_str( "\n \x1b [31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b [0m\n " ) ;
727+ message_box. write_to( & mut error_message) ;
728+ error_message. push_str( "\n \x1b [31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\x1b [0m\n " ) ;
729+
730+ panic!( "{}" , error_message) ;
731+ }
733732
734- panic!( "{}" , error_message) ;
733+ // this one is _actually_ meant to exist once per shared object
734+ static COMPATIBILITY_CHECK_ONCE : std:: sync:: Once = std:: sync:: Once :: new( ) ;
735+ COMPATIBILITY_CHECK_ONCE . call_once( || {
736+ check_compatibility( ) ;
737+ } ) ;
735738 }
736739 } ;
737740}
0 commit comments