1- use std:: collections:: { HashMap , HashSet } ;
1+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
22
33use derive_builder:: Builder ;
44use rust_team_data:: v1;
@@ -13,7 +13,7 @@ use crate::github::{
1313 RepoDiff , SyncGitHub , TeamDiff , api, construct_branch_protection, convert_permission,
1414} ;
1515
16- const DEFAULT_ORG : & str = "rust-lang" ;
16+ pub const DEFAULT_ORG : & str = "rust-lang" ;
1717
1818type UserId = u64 ;
1919
@@ -74,11 +74,12 @@ impl DataModel {
7474 . map ( |user| ( user. github_id , user. name . clone ( ) ) )
7575 . collect ( ) ;
7676
77- let mut team_memberships : HashMap < String , HashMap < UserId , TeamMember > > = HashMap :: default ( ) ;
78- let mut teams = vec ! [ ] ;
77+ let mut orgs : HashMap < String , GithubOrg > = HashMap :: default ( ) ;
78+
7979 for team in & self . teams {
8080 for gh_team in & team. gh_teams {
81- let res = team_memberships. insert (
81+ let org = orgs. entry ( gh_team. org . clone ( ) ) . or_default ( ) ;
82+ let res = org. team_memberships . insert (
8283 gh_team. name . clone ( ) ,
8384 gh_team
8485 . members
@@ -96,27 +97,26 @@ impl DataModel {
9697 ) ;
9798 assert ! ( res. is_none( ) ) ;
9899
99- teams. push ( api:: Team {
100- id : Some ( teams. len ( ) as u64 ) ,
100+ org . teams . push ( api:: Team {
101+ id : Some ( org . teams . len ( ) as u64 ) ,
101102 name : gh_team. name . clone ( ) ,
102103 description : Some ( "Managed by the rust-lang/team repository." . to_string ( ) ) ,
103104 privacy : TeamPrivacy :: Closed ,
104105 slug : gh_team. name . clone ( ) ,
105- } )
106+ } ) ;
107+
108+ org. members . extend ( gh_team. members . iter ( ) . copied ( ) ) ;
106109 }
107110 }
108111
109- let mut repos = HashMap :: default ( ) ;
110- let mut repo_members: HashMap < String , RepoMembers > = HashMap :: default ( ) ;
111- let mut branch_protections = HashMap :: new ( ) ;
112-
113112 for repo in & self . repos {
114- repos. insert (
113+ let org = orgs. entry ( repo. org . clone ( ) ) . or_default ( ) ;
114+ org. repos . insert (
115115 repo. name . clone ( ) ,
116116 Repo {
117- node_id : repos. len ( ) . to_string ( ) ,
117+ node_id : org . repos . len ( ) . to_string ( ) ,
118118 name : repo. name . clone ( ) ,
119- org : DEFAULT_ORG . to_string ( ) ,
119+ org : repo . org . clone ( ) ,
120120 description : repo. description . clone ( ) ,
121121 homepage : repo. homepage . clone ( ) ,
122122 archived : false ,
@@ -146,7 +146,8 @@ impl DataModel {
146146 permission : convert_permission ( & m. permission ) ,
147147 } )
148148 . collect ( ) ;
149- repo_members. insert ( repo. name . clone ( ) , RepoMembers { teams, members } ) ;
149+ org. repo_members
150+ . insert ( repo. name . clone ( ) , RepoMembers { teams, members } ) ;
150151
151152 let repo_v1: v1:: Repo = repo. clone ( ) . into ( ) ;
152153 let mut protections = vec ! [ ] ;
@@ -156,19 +157,15 @@ impl DataModel {
156157 construct_branch_protection ( & repo_v1, protection) ,
157158 ) ) ;
158159 }
159- branch_protections. insert ( repo. name . clone ( ) , protections) ;
160+ org. branch_protections
161+ . insert ( repo. name . clone ( ) , protections) ;
160162 }
161163
162- GithubMock {
163- users,
164- owners : Default :: default ( ) ,
165- teams,
166- team_memberships,
167- team_invitations : Default :: default ( ) ,
168- repos,
169- repo_members,
170- branch_protections,
164+ if orgs. is_empty ( ) {
165+ orgs. insert ( DEFAULT_ORG . to_string ( ) , GithubOrg :: default ( ) ) ;
171166 }
167+
168+ GithubMock { users, orgs }
172169 }
173170
174171 pub fn diff_teams ( & self , github : GithubMock ) -> Vec < TeamDiff > {
@@ -249,10 +246,10 @@ impl From<TeamData> for v1::Team {
249246}
250247
251248impl TeamDataBuilder {
252- pub fn gh_team ( mut self , name : & str , members : & [ UserId ] ) -> Self {
249+ pub fn gh_team ( mut self , org : & str , name : & str , members : & [ UserId ] ) -> Self {
253250 let mut gh_teams = self . gh_teams . unwrap_or_default ( ) ;
254251 gh_teams. push ( GitHubTeam {
255- org : DEFAULT_ORG . to_string ( ) ,
252+ org : org . to_string ( ) ,
256253 name : name. to_string ( ) ,
257254 members : members. to_vec ( ) ,
258255 } ) ;
@@ -265,6 +262,8 @@ impl TeamDataBuilder {
265262#[ builder( pattern = "owned" ) ]
266263pub struct RepoData {
267264 name : String ,
265+ #[ builder( default = DEFAULT_ORG . to_string( ) ) ]
266+ org : String ,
268267 #[ builder( default ) ]
269268 pub description : String ,
270269 #[ builder( default ) ]
@@ -307,6 +306,7 @@ impl From<RepoData> for v1::Repo {
307306 fn from ( value : RepoData ) -> Self {
308307 let RepoData {
309308 name,
309+ org,
310310 description,
311311 homepage,
312312 bots,
@@ -317,7 +317,7 @@ impl From<RepoData> for v1::Repo {
317317 branch_protections,
318318 } = value;
319319 Self {
320- org : DEFAULT_ORG . to_string ( ) ,
320+ org,
321321 name : name. clone ( ) ,
322322 description,
323323 homepage,
@@ -411,28 +411,30 @@ impl BranchProtectionBuilder {
411411pub struct GithubMock {
412412 // user ID -> login
413413 users : HashMap < UserId , String > ,
414- // org name -> user ID
415- owners : HashMap < String , Vec < UserId > > ,
416- teams : Vec < Team > ,
417- // Team name -> members
418- team_memberships : HashMap < String , HashMap < UserId , TeamMember > > ,
419- // Team name -> list of invited users
420- team_invitations : HashMap < String , Vec < String > > ,
421- // Repo name -> repo data
422- repos : HashMap < String , Repo > ,
423- // Repo name -> (teams, members)
424- repo_members : HashMap < String , RepoMembers > ,
425- // Repo name -> Vec<(protection ID, branch protection)>
426- branch_protections : HashMap < String , Vec < ( String , BranchProtection ) > > ,
414+ // org name -> organization data
415+ orgs : HashMap < String , GithubOrg > ,
427416}
428417
429418impl GithubMock {
430- pub fn add_invitation ( & mut self , repo : & str , user : & str ) {
431- self . team_invitations
419+ pub fn add_invitation ( & mut self , org : & str , repo : & str , user : & str ) {
420+ self . get_org_mut ( org)
421+ . team_invitations
432422 . entry ( repo. to_string ( ) )
433423 . or_default ( )
434424 . push ( user. to_string ( ) ) ;
435425 }
426+
427+ fn get_org ( & self , org : & str ) -> & GithubOrg {
428+ self . orgs
429+ . get ( org)
430+ . unwrap_or_else ( || panic ! ( "Org {org} not found" ) )
431+ }
432+
433+ fn get_org_mut ( & mut self , org : & str ) -> & mut GithubOrg {
434+ self . orgs
435+ . get_mut ( org)
436+ . unwrap_or_else ( || panic ! ( "Org {org} not found" ) )
437+ }
436438}
437439
438440impl GithubRead for GithubMock {
@@ -446,48 +448,47 @@ impl GithubRead for GithubMock {
446448 }
447449
448450 fn org_owners ( & self , org : & str ) -> anyhow:: Result < HashSet < UserId > > {
449- Ok ( self
450- . owners
451- . get ( org)
452- . cloned ( )
453- . unwrap_or_default ( )
454- . into_iter ( )
455- . collect ( ) )
451+ Ok ( self . get_org ( org) . owners . iter ( ) . copied ( ) . collect ( ) )
456452 }
457453
458454 fn org_teams ( & self , org : & str ) -> anyhow:: Result < Vec < ( String , String ) > > {
459- assert_eq ! ( org, DEFAULT_ORG ) ;
460455 Ok ( self
456+ . get_org ( org)
461457 . teams
462458 . iter ( )
463459 . map ( |team| ( team. name . clone ( ) , team. slug . clone ( ) ) )
464460 . collect ( ) )
465461 }
466462
467- fn team ( & self , _org : & str , team : & str ) -> anyhow:: Result < Option < Team > > {
468- Ok ( self . teams . iter ( ) . find ( |t| t. name == team) . cloned ( ) )
463+ fn team ( & self , org : & str , team : & str ) -> anyhow:: Result < Option < Team > > {
464+ Ok ( self
465+ . get_org ( org)
466+ . teams
467+ . iter ( )
468+ . find ( |t| t. name == team)
469+ . cloned ( ) )
469470 }
470471
471472 fn team_memberships (
472473 & self ,
473474 team : & Team ,
474- _org : & str ,
475+ org : & str ,
475476 ) -> anyhow:: Result < HashMap < UserId , TeamMember > > {
476- let memberships = self
477+ Ok ( self
478+ . get_org ( org)
477479 . team_memberships
478480 . get ( & team. name )
479481 . cloned ( )
480- . unwrap_or_default ( ) ;
481- Ok ( memberships)
482+ . unwrap_or_default ( ) )
482483 }
483484
484485 fn team_membership_invitations (
485486 & self ,
486487 org : & str ,
487488 team : & str ,
488489 ) -> anyhow:: Result < HashSet < String > > {
489- assert_eq ! ( org, DEFAULT_ORG ) ;
490490 Ok ( self
491+ . get_org ( org)
491492 . team_invitations
492493 . get ( team)
493494 . cloned ( )
@@ -497,13 +498,15 @@ impl GithubRead for GithubMock {
497498 }
498499
499500 fn repo ( & self , org : & str , repo : & str ) -> anyhow:: Result < Option < Repo > > {
500- assert_eq ! ( org, DEFAULT_ORG ) ;
501- Ok ( self . repos . get ( repo) . cloned ( ) )
501+ Ok ( self
502+ . orgs
503+ . get ( org)
504+ . and_then ( |org| org. repos . get ( repo) . cloned ( ) ) )
502505 }
503506
504507 fn repo_teams ( & self , org : & str , repo : & str ) -> anyhow:: Result < Vec < RepoTeam > > {
505- assert_eq ! ( org, DEFAULT_ORG ) ;
506508 Ok ( self
509+ . get_org ( org)
507510 . repo_members
508511 . get ( repo)
509512 . cloned ( )
@@ -512,9 +515,8 @@ impl GithubRead for GithubMock {
512515 }
513516
514517 fn repo_collaborators ( & self , org : & str , repo : & str ) -> anyhow:: Result < Vec < RepoUser > > {
515- // The mock currently only supports mocking one GitHub organization.
516- assert_eq ! ( org, DEFAULT_ORG ) ;
517518 Ok ( self
519+ . get_org ( org)
518520 . repo_members
519521 . get ( repo)
520522 . cloned ( )
@@ -527,9 +529,7 @@ impl GithubRead for GithubMock {
527529 org : & str ,
528530 repo : & str ,
529531 ) -> anyhow:: Result < HashMap < String , ( String , BranchProtection ) > > {
530- assert_eq ! ( org, DEFAULT_ORG ) ;
531-
532- let Some ( protections) = self . branch_protections . get ( repo) else {
532+ let Some ( protections) = self . get_org ( org) . branch_protections . get ( repo) else {
533533 return Ok ( Default :: default ( ) ) ;
534534 } ;
535535 let mut result = HashMap :: default ( ) ;
@@ -541,6 +541,23 @@ impl GithubRead for GithubMock {
541541 }
542542}
543543
544+ #[ derive( Default ) ]
545+ struct GithubOrg {
546+ members : BTreeSet < UserId > ,
547+ owners : BTreeSet < UserId > ,
548+ teams : Vec < Team > ,
549+ // Team name -> list of invited users
550+ team_invitations : HashMap < String , Vec < String > > ,
551+ // Team name -> members
552+ team_memberships : HashMap < String , HashMap < UserId , TeamMember > > ,
553+ // Repo name -> repo data
554+ repos : HashMap < String , Repo > ,
555+ // Repo name -> (teams, members)
556+ repo_members : HashMap < String , RepoMembers > ,
557+ // Repo name -> Vec<(protection ID, branch protection)>
558+ branch_protections : HashMap < String , Vec < ( String , BranchProtection ) > > ,
559+ }
560+
544561#[ derive( Clone ) ]
545562pub struct RepoMembers {
546563 teams : Vec < RepoTeam > ,
0 commit comments