Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ toml = "0.9"
serde_json = "1.0"
rust_team_data = { git = "https://github.com/rust-lang/team" }
percent-encoding = "2.3.2"
rayon = "1"

[dev-dependencies]
time = { version = "0.3.44", features = ["parsing"] }
7 changes: 6 additions & 1 deletion locales/en-US/governance.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ governance-members-header = Members
governance-alumni-header = Alumni
governance-alumni-thanks = We also want to thank all past members for their invaluable contributions!
## governance/all-team-members.mbs
## governance/all-team-members.hbs
governance-all-team-members-title = All Rust team members
governance-all-team-members-intro = This section lists the members of currently active Rust teams.
governance-all-team-members-alumni-intro = This section lists our team alumni.
## govenance/person.hbs
governance-person-title = Rust Project team member
governance-person-team-member = Team member
governance-person-team-alumni = Alumni
29 changes: 29 additions & 0 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use crate::{BaseUrl, ENGLISH, LAYOUT};
use anyhow::Context;
use handlebars::Handlebars;
use handlebars_fluent::{Loader, SimpleLoader};
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use serde::Serialize;
use std::ffi::OsStr;
use std::fs::File;
Expand Down Expand Up @@ -180,11 +182,14 @@ pub fn render_index(render_ctx: &RenderCtx) -> anyhow::Result<()> {
pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
let data = render_ctx.teams.index_data();

// Index page
for_all_langs("governance/index.html", |dst_path, lang| {
render_ctx
.page("governance/index", "governance-page-title", &data, lang)
.render(dst_path)
})?;

// Individual teams
for team in data.teams {
let data: PageData = render_ctx
.teams
Expand All @@ -207,6 +212,7 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
)?;
}

// Archived teams
let archived_teams_data = render_ctx.teams.archived_teams();
for_all_langs("governance/archived-teams.html", |dst_path, lang| {
render_ctx
Expand All @@ -219,6 +225,7 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
.render(dst_path)
})?;

// Page with all team members
let all_team_members_data = render_ctx.teams.all_team_members();
for_all_langs("governance/all-team-members.html", |dst_path, lang| {
render_ctx
Expand All @@ -231,6 +238,28 @@ pub fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
.render(dst_path)
})?;

// A specific page for each team member
let all_person_data = render_ctx.teams.all_person_data();
all_person_data
.par_iter()
.map(|person| {
// Use <username>/index.html for a nicer URL (/people/foo vs /people/foo.html).
for_all_langs(
&format!("governance/people/{}/index.html", person.github),
|dst_path, lang| {
render_ctx
.page(
"governance/person",
"governance-person-title",
&person,
lang,
)
.render(dst_path)
},
)
})
.collect::<anyhow::Result<Vec<_>>>()?;

Ok(())
}

Expand Down
165 changes: 159 additions & 6 deletions src/teams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,11 @@ impl RustTeams {
.filter(|team| team.website_data.is_some())
// On the main page, show the leadership-council and all top-level
// teams.
.filter(|team| team.kind == TeamKind::Team && team.subteam_of.is_none())
.filter(is_toplevel_team)
.map(|team| IndexTeam {
section: kind_to_str(team.kind),
page_name: team.website_data.clone().unwrap().page,
url: format!(
"{}/{}",
kind_to_str(team.kind),
team.website_data.as_ref().unwrap().page
),
url: get_team_relative_url(&team),
team,
})
.collect::<Vec<IndexTeam>>();
Expand Down Expand Up @@ -234,6 +230,111 @@ impl RustTeams {

AllTeamMembers { active, alumni }
}

pub fn all_person_data(&self) -> Vec<PersonData> {
let mut people: HashMap<String, PersonData> = HashMap::new();

enum TeamMode {
Member,
Alumni,
MemberOfArchivedTeam,
}

fn add_team(
people: &mut HashMap<String, PersonData>,
ctx: &RustTeams,
member: &TeamMember,
team: &Team,
mode: TeamMode,
) {
let person = people
.entry(member.github.clone())
.or_insert_with(move || PersonData {
name: member.name.clone(),
github: member.github.clone(),
active_teams: vec![],
alumni_teams: vec![],
});
let teams = match mode {
TeamMode::Member => &mut person.active_teams,
TeamMode::Alumni | TeamMode::MemberOfArchivedTeam => &mut person.alumni_teams,
};
let url = match mode {
TeamMode::Member | TeamMode::Alumni => ctx.get_toplevel_team_url(team),
TeamMode::MemberOfArchivedTeam => Some("archived-teams.html".to_string()),
};
teams.push(PersonTeam::new(team, member, url));
}

for team in &self.archived_teams {
for member in team.members.iter().chain(&team.alumni) {
add_team(
&mut people,
self,
member,
team,
TeamMode::MemberOfArchivedTeam,
);
}
}
for team in &self.teams {
if team.kind == TeamKind::MarkerTeam && team.website_data.is_none() {
continue;
}

for member in &team.members {
add_team(&mut people, self, member, team, TeamMode::Member);
}
for member in &team.alumni {
add_team(&mut people, self, member, team, TeamMode::Alumni);
}
}

let mut people: Vec<PersonData> = people.into_values().collect();
people.sort_by(|a, b| a.github.cmp(&b.github));

for person in &mut people {
person
.active_teams
.sort_by(|a, b| a.webpage_name.cmp(&b.webpage_name));
person
.alumni_teams
.sort_by(|a, b| a.webpage_name.cmp(&b.webpage_name));
}

people
}

fn get_toplevel_team_url<'a>(&'a self, mut team: &'a Team) -> Option<String> {
while !is_toplevel_team(team) {
let Some(parent) = &team.subteam_of else {
return None;
};
let parent = self.teams.iter().find(|t| t.name == *parent)?;
team = parent;
}

if team.website_data.is_some() {
Some(get_team_relative_url(team))
} else {
None
}
}
}

/// Get a relative URL of a team that should be appended to
/// Should only be used for top-level teams.
fn get_team_relative_url(team: &Team) -> String {
assert!(is_toplevel_team(team));
format!(
"{}/{}",
kind_to_str(team.kind),
team.website_data.as_ref().unwrap().page
)
}

fn is_toplevel_team(team: &Team) -> bool {
team.kind == TeamKind::Team && team.subteam_of.is_none()
}

#[derive(Serialize)]
Expand Down Expand Up @@ -272,6 +373,58 @@ pub struct AllTeamMembers {
alumni: Vec<TeamMember>,
}

#[derive(Serialize)]
pub struct PersonTeam {
team: Team,
toplevel_url: Option<String>,
webpage_name: String,
roles: Vec<String>,
}

impl PersonTeam {
fn new(team: &Team, member: &TeamMember, toplevel_url: Option<String>) -> Self {
// Turn inside-rust-reviewers into Inside Rust Reviewers
let normalize_name = |name: &str| {
name.split("-")
.map(|p| {
p.chars()
.take(1)
.flat_map(|c| c.to_uppercase())
.chain(p.chars().skip(1))
.collect::<String>()
})
.collect::<Vec<String>>()
.join(" ")
};

let webpage_name = team
.website_data
.as_ref()
.map(|w| w.name.clone())
.unwrap_or_else(|| normalize_name(&team.name));

let mut roles = vec![];
if member.is_lead {
roles.push("Lead".to_string());
}
roles.extend(member.roles.iter().map(|r| normalize_name(r)));
Self {
team: team.clone(),
toplevel_url,
webpage_name,
roles,
}
}
}

#[derive(Serialize)]
pub struct PersonData {
name: String,
pub github: String,
active_teams: Vec<PersonTeam>,
alumni_teams: Vec<PersonTeam>,
}

pub fn load_rust_teams() -> anyhow::Result<RustTeams> {
println!("Downloading Team API data");

Expand Down
8 changes: 4 additions & 4 deletions templates/governance/all-team-members.html.hbs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{{#*inline "member"}}
<div class="w-100 w-25-l mb3 flex flex-row items-center">
<a class="mr4 w3 h3 flex-shrink-0" href="https://github.com/{{member.github}}">
<a class="mr4 w3 h3 flex-shrink-0" href="{{baseurl}}/governance/people/{{member.github}}">
<img class="w-100 h-100 bg-white br2"
src="https://avatars.githubusercontent.com/{{member.github}}"
alt="{{member.name}}">
</a>
<div>
{{member.name}}
<a href="{{baseurl}}/governance/people/{{member.github}}">{{member.name}}</a>
<div class="f4">
GitHub: <a href="https://github.com/{{member.github}}">{{member.github}}</a>
</div>
Expand All @@ -24,7 +24,7 @@
<section class="green" style="padding-bottom: 15px;">
<div class="w-100 mw-none mw-8-m mw9-l flex flex-column flex-row-l flex-wrap-l center">
{{#each data.active as |member|}}
{{> member member=member }}
{{> member member=member baseurl=../baseurl }}
{{/each}}
</div>
</section>
Expand All @@ -38,7 +38,7 @@
<section class="red" style="padding-bottom: 15px;">
<div class="w-100 mw-none mw-8-m mw9-l flex flex-column flex-row-l flex-wrap-l center">
{{#each data.alumni as |member|}}
{{> member member=member }}
{{> member member=member baseurl=../baseurl }}
{{/each}}
</div>
</section>
Expand Down
Loading