@@ -2,6 +2,7 @@ use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContex
2
2
use percent_encoding:: { utf8_percent_encode, AsciiSet , NON_ALPHANUMERIC } ;
3
3
use rust_team_data:: v1:: { Team , TeamKind , Teams , BASE_URL } ;
4
4
use std:: cmp:: Reverse ;
5
+ use std:: collections:: HashMap ;
5
6
use std:: error:: Error ;
6
7
use std:: fmt;
7
8
use std:: time:: Instant ;
@@ -101,25 +102,78 @@ impl Data {
101
102
return Err ( TeamNotFound . into ( ) ) ;
102
103
}
103
104
104
- // Then find all the subteams and wgs
105
- let mut subteams = Vec :: new ( ) ;
105
+ // Then find all the subteams, working groups and project groups.
106
+ let mut raw_subteams = Vec :: new ( ) ;
106
107
let mut wgs = Vec :: new ( ) ;
107
108
let mut project_groups = Vec :: new ( ) ;
109
+
110
+ let superteams: HashMap < _ , _ > = self
111
+ . teams
112
+ . iter ( )
113
+ . filter ( |team| matches ! ( team. kind, TeamKind :: Team ) )
114
+ . filter_map ( |team| Some ( ( team. name . clone ( ) , team. subteam_of . clone ( ) ?) ) )
115
+ . collect ( ) ;
116
+
108
117
self . teams
109
118
. into_iter ( )
110
119
. filter ( |team| team. website_data . is_some ( ) )
111
- . filter ( |team| team. subteam_of . as_ref ( ) == Some ( & main_team. name ) )
120
+ . filter ( |team| {
121
+ // For teams find not only direct subteams but also transitive ones.
122
+ if let TeamKind :: Team = team. kind {
123
+ let mut team = & team. name ;
124
+
125
+ // The graph of teams is guaranteed to be acyclic by the CI script of
126
+ // the team repository. Therefore this loop has to terminate eventually.
127
+ while let Some ( superteam) = superteams. get ( team) {
128
+ if superteam == & main_team. name {
129
+ return true ;
130
+ }
131
+
132
+ team = superteam;
133
+ }
134
+
135
+ return false ;
136
+ }
137
+
138
+ team. subteam_of . as_ref ( ) == Some ( & main_team. name )
139
+ } )
112
140
. for_each ( |team| match team. kind {
113
- TeamKind :: Team => subteams . push ( team) ,
141
+ TeamKind :: Team => raw_subteams . push ( team) ,
114
142
TeamKind :: WorkingGroup => wgs. push ( team) ,
115
143
TeamKind :: ProjectGroup => project_groups. push ( team) ,
116
144
_ => { }
117
145
} ) ;
118
146
119
- subteams . sort_by_key ( |team| Reverse ( team. website_data . as_ref ( ) . unwrap ( ) . weight ) ) ;
147
+ raw_subteams . sort_by_key ( |team| Reverse ( team. website_data . as_ref ( ) . unwrap ( ) . weight ) ) ;
120
148
wgs. sort_by_key ( |team| Reverse ( team. website_data . as_ref ( ) . unwrap ( ) . weight ) ) ;
121
149
project_groups. sort_by_key ( |team| Reverse ( team. website_data . as_ref ( ) . unwrap ( ) . weight ) ) ;
122
150
151
+ // Lay out subteams according to their hierarchy.
152
+ // Superteams come first and subteams come after, recursively.
153
+ // The ordering of sibling teams is unaffected, they remain sorted first by weight, then
154
+ // alphabetically if we assume that the Team API serves the teams alphabetically.
155
+ let mut subteams = Vec :: with_capacity ( raw_subteams. len ( ) ) ;
156
+ lay_out_subteams_hierarchically ( & mut subteams, None , & main_team. name , & raw_subteams) ;
157
+
158
+ fn lay_out_subteams_hierarchically < ' a > (
159
+ result : & mut Vec < Team > ,
160
+ team : Option < & Team > ,
161
+ main_team : & str ,
162
+ subteams : & [ Team ] ,
163
+ ) {
164
+ let name = team. map ( |team| team. name . as_str ( ) ) . or ( Some ( main_team) ) ;
165
+
166
+ if let Some ( team) = team {
167
+ result. push ( team. clone ( ) ) ;
168
+ }
169
+
170
+ for subteam in subteams {
171
+ if subteam. subteam_of . as_deref ( ) == name {
172
+ lay_out_subteams_hierarchically ( result, Some ( subteam) , main_team, subteams) ;
173
+ }
174
+ }
175
+ }
176
+
123
177
Ok ( PageData {
124
178
team : main_team,
125
179
zulip_domain : crate :: ZULIP_DOMAIN ,
0 commit comments