@@ -681,10 +681,44 @@ fn generate_contributors_js(
681681 js. push_str ( "// This file is auto-generated by the tags preprocessor\n " ) ;
682682 js. push_str ( "const contributorsIndex = {\n " ) ;
683683
684+ // Load the contributors database once
685+ let contributors_db = load_contributors_db ( ) ?;
686+
687+ // Create a lookup from displayName/name to ID to help match contributors
688+ let mut name_to_id: HashMap < String , String > = HashMap :: new ( ) ;
689+ let mut id_to_display_name: HashMap < String , String > = HashMap :: new ( ) ;
690+
691+ for ( id, data) in & contributors_db {
692+ // Map the ID directly
693+ name_to_id. insert ( id. clone ( ) , id. clone ( ) ) ;
694+
695+ // Get display name for later use
696+ let display_name = data. get ( "displayName" )
697+ . and_then ( |v| v. as_str ( ) )
698+ . or_else ( || data. get ( "name" ) . and_then ( |v| v. as_str ( ) ) )
699+ . unwrap_or ( id) ;
700+
701+ id_to_display_name. insert ( id. clone ( ) , display_name. to_string ( ) ) ;
702+
703+ // Map displayName to ID if available
704+ if let Some ( display_name) = data. get ( "displayName" ) . and_then ( |v| v. as_str ( ) ) {
705+ name_to_id. insert ( display_name. to_string ( ) , id. clone ( ) ) ;
706+ }
707+
708+ // Map name to ID if available
709+ if let Some ( name) = data. get ( "name" ) . and_then ( |v| v. as_str ( ) ) {
710+ name_to_id. insert ( name. to_string ( ) , id. clone ( ) ) ;
711+ }
712+ }
713+
714+ // Keep track of which contributors we've already processed
715+ let mut processed_contributors = std:: collections:: HashSet :: new ( ) ;
716+
684717 // Sort contributors alphabetically for consistency
685718 let mut sorted_contributors: Vec < _ > = contributors_map. iter ( ) . collect ( ) ;
686719 sorted_contributors. sort_by ( |a, b| a. 0 . cmp ( & b. 0 ) ) ;
687720
721+ // First add all contributors who have contributed to chapters
688722 for ( i, ( contributor, chapters) ) in sorted_contributors. iter ( ) . enumerate ( ) {
689723 js. push_str ( & format ! ( " \" {}\" : {{\n " , contributor) ) ;
690724 js. push_str ( & format ! ( " \" chapters\" : [\n " ) ) ;
@@ -731,10 +765,139 @@ fn generate_contributors_js(
731765 }
732766 }
733767
768+ // Try to find the contributor in the database by mapped ID
769+ let contributor_id = name_to_id. get ( * contributor) . cloned ( ) ;
770+
771+ if let Some ( db_id) = & contributor_id {
772+ if let Some ( contributor_data) = contributors_db. get ( db_id) {
773+ // Add company if available
774+ if let Some ( company) = contributor_data. get ( "company" ) . and_then ( |v| v. as_str ( ) ) {
775+ if !company. is_empty ( ) {
776+ js. push_str ( & format ! ( ",\n \" company\" : \" {}\" " , company) ) ;
777+ }
778+ }
779+
780+ // Add role if available
781+ if let Some ( role) = contributor_data. get ( "role" ) . and_then ( |v| v. as_str ( ) ) {
782+ if !role. is_empty ( ) {
783+ js. push_str ( & format ! ( ",\n \" role\" : \" {}\" " , role) ) ;
784+ }
785+ }
786+
787+ // Add description if available
788+ if let Some ( description) = contributor_data. get ( "description" ) . and_then ( |v| v. as_str ( ) ) {
789+ if !description. is_empty ( ) {
790+ js. push_str ( & format ! ( ",\n \" description\" : \" {}\" " , description) ) ;
791+ }
792+ }
793+
794+ // Add features if available
795+ if let Some ( features) = contributor_data. get ( "features" ) . and_then ( |v| v. as_array ( ) ) {
796+ let features_str: Vec < String > = features. iter ( ) . filter_map ( |v| v. as_str ( ) . map ( String :: from) ) . collect ( ) ;
797+ if !features_str. is_empty ( ) {
798+ js. push_str ( & format ! ( ",\n \" features\" : [{}]" , features_str. iter( ) . map( |f| format!( "\" {}\" " , f) ) . collect:: <Vec <_>>( ) . join( ", " ) ) ) ;
799+ }
800+ }
801+ }
802+ }
803+
734804 js. push_str ( "\n }" ) ;
735805
736806 if i < sorted_contributors. len ( ) - 1 {
737807 js. push_str ( ",\n " ) ;
808+ } else {
809+ // If we're the last one and there will be more contributors after this,
810+ // add a comma
811+ if processed_contributors. len ( ) < contributors_db. len ( ) {
812+ js. push_str ( ",\n " ) ;
813+ } else {
814+ js. push_str ( "\n " ) ;
815+ }
816+ }
817+
818+ // Mark this contributor as processed
819+ if let Some ( db_id) = contributor_id {
820+ processed_contributors. insert ( db_id) ;
821+ }
822+ }
823+
824+ // Now add contributors from the database who haven't been processed yet
825+ // These are contributors who haven't contributed to any chapters yet
826+ let mut additional_contributors: Vec < _ > = contributors_db. iter ( )
827+ . filter ( |( id, _) | !processed_contributors. contains ( * id) )
828+ . collect ( ) ;
829+ additional_contributors. sort_by ( |a, b| a. 0 . cmp ( & b. 0 ) ) ;
830+
831+ for ( i, ( id, data) ) in additional_contributors. iter ( ) . enumerate ( ) {
832+ // Get display name
833+ let display_name = id_to_display_name. get ( * id) . unwrap_or ( id) ;
834+
835+ js. push_str ( & format ! ( " \" {}\" : {{\n " , display_name) ) ;
836+
837+ // Add empty chapters array
838+ js. push_str ( " \" chapters\" : []" ) ;
839+
840+ // Add github profile if available
841+ if let Some ( github) = data. get ( "github" ) . and_then ( |v| v. as_str ( ) ) {
842+ if !github. is_empty ( ) {
843+ js. push_str ( & format ! ( ",\n \" github\" : \" {}\" " , github) ) ;
844+ }
845+ }
846+
847+ // Add avatar URL if available
848+ if let Some ( avatar) = data. get ( "avatar" ) . and_then ( |v| v. as_str ( ) ) {
849+ if !avatar. is_empty ( ) {
850+ js. push_str ( & format ! ( ",\n \" avatar\" : \" {}\" " , avatar) ) ;
851+ }
852+ }
853+
854+ // Add Twitter profile if available
855+ if let Some ( twitter) = data. get ( "twitter" ) . and_then ( |v| v. as_str ( ) ) {
856+ if !twitter. is_empty ( ) {
857+ js. push_str ( & format ! ( ",\n \" twitter\" : \" {}\" " , twitter) ) ;
858+ }
859+ }
860+
861+ // Add website if available
862+ if let Some ( website) = data. get ( "website" ) . and_then ( |v| v. as_str ( ) ) {
863+ if !website. is_empty ( ) {
864+ js. push_str ( & format ! ( ",\n \" website\" : \" {}\" " , website) ) ;
865+ }
866+ }
867+
868+ // Add company if available
869+ if let Some ( company) = data. get ( "company" ) . and_then ( |v| v. as_str ( ) ) {
870+ if !company. is_empty ( ) {
871+ js. push_str ( & format ! ( ",\n \" company\" : \" {}\" " , company) ) ;
872+ }
873+ }
874+
875+ // Add role if available
876+ if let Some ( role) = data. get ( "role" ) . and_then ( |v| v. as_str ( ) ) {
877+ if !role. is_empty ( ) {
878+ js. push_str ( & format ! ( ",\n \" role\" : \" {}\" " , role) ) ;
879+ }
880+ }
881+
882+ // Add description if available
883+ if let Some ( description) = data. get ( "description" ) . and_then ( |v| v. as_str ( ) ) {
884+ if !description. is_empty ( ) {
885+ js. push_str ( & format ! ( ",\n \" description\" : \" {}\" " , description) ) ;
886+ }
887+ }
888+
889+ // Add features if available
890+ if let Some ( features) = data. get ( "features" ) . and_then ( |v| v. as_array ( ) ) {
891+ let features_str: Vec < String > = features. iter ( ) . filter_map ( |v| v. as_str ( ) . map ( String :: from) ) . collect ( ) ;
892+ if !features_str. is_empty ( ) {
893+ js. push_str ( & format ! ( ",\n \" features\" : [{}]" , features_str. iter( ) . map( |f| format!( "\" {}\" " , f) ) . collect:: <Vec <_>>( ) . join( ", " ) ) ) ;
894+ }
895+ }
896+
897+ js. push_str ( "\n }" ) ;
898+
899+ if i < additional_contributors. len ( ) - 1 {
900+ js. push_str ( ",\n " ) ;
738901 } else {
739902 js. push_str ( "\n " ) ;
740903 }
0 commit comments