Skip to content

Commit e6fc0f6

Browse files
authored
Merge pull request #1857 from fmease/subsubteams
Support subteams that have subteams
2 parents 41a2bb7 + a77d2fa commit e6fc0f6

File tree

1 file changed

+59
-5
lines changed

1 file changed

+59
-5
lines changed

src/teams.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContex
22
use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};
33
use rust_team_data::v1::{Team, TeamKind, Teams, BASE_URL};
44
use std::cmp::Reverse;
5+
use std::collections::HashMap;
56
use std::error::Error;
67
use std::fmt;
78
use std::time::Instant;
@@ -101,25 +102,78 @@ impl Data {
101102
return Err(TeamNotFound.into());
102103
}
103104

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();
106107
let mut wgs = Vec::new();
107108
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+
108117
self.teams
109118
.into_iter()
110119
.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+
})
112140
.for_each(|team| match team.kind {
113-
TeamKind::Team => subteams.push(team),
141+
TeamKind::Team => raw_subteams.push(team),
114142
TeamKind::WorkingGroup => wgs.push(team),
115143
TeamKind::ProjectGroup => project_groups.push(team),
116144
_ => {}
117145
});
118146

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));
120148
wgs.sort_by_key(|team| Reverse(team.website_data.as_ref().unwrap().weight));
121149
project_groups.sort_by_key(|team| Reverse(team.website_data.as_ref().unwrap().weight));
122150

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+
123177
Ok(PageData {
124178
team: main_team,
125179
zulip_domain: crate::ZULIP_DOMAIN,

0 commit comments

Comments
 (0)