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
22 changes: 22 additions & 0 deletions crates/mdbook-goals/src/mdbook_preprocessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use mdbook::BookItem;
use regex::{Captures, Regex};
use rust_project_goals::config::Configuration;
use rust_project_goals::format_team_ask::format_team_asks;
use rust_project_goals::format_champions::format_champions;
use rust_project_goals::util::{self, GithubUserInfo};

use rust_project_goals::spanned::Spanned;
Expand Down Expand Up @@ -147,6 +148,7 @@ impl<'c> GoalPreprocessorWithContext<'c> {
match book_item {
BookItem::Chapter(chapter) => {
self.replace_metadata_placeholders(chapter)?;
self.replace_champions(chapter)?;
self.replace_team_asks(chapter)?;
self.replace_valid_team_asks(chapter)?;
self.replace_goal_lists(chapter)?;
Expand Down Expand Up @@ -308,6 +310,26 @@ impl<'c> GoalPreprocessorWithContext<'c> {
}
}

/// Look for `(((CHAMPIONS)))` in the chapter content and replace it with the champions table.
fn replace_champions(&mut self, chapter: &mut Chapter) -> anyhow::Result<()> {
let Some(m) = re::CHAMPIONS.find(&chapter.content) else {
return Ok(());
};
let range = m.range();

let Some(path) = &chapter.path else {
anyhow::bail!("found `(((CHAMPIONS)))` but chapter has no path")
};

let goals = self.goal_documents(path)?;
let goal_refs: Vec<&GoalDocument> = goals.iter().collect();
let format_champions =
format_champions(&goal_refs).map_err(|e| anyhow::anyhow!("{e}"))?;
chapter.content.replace_range(range, &format_champions);

Ok(())
}

/// Look for `<!-- TEAM ASKS -->` in the chapter content and replace it with the team asks.
fn replace_team_asks(&mut self, chapter: &mut Chapter) -> anyhow::Result<()> {
let Some(m) = re::TEAM_ASKS.find(&chapter.content) else {
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-project-goals-cli/src/csv_reports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn champions(repository: &Repository, milestone: &str) -> Result<()> {
let rows: Vec<ChampionRow> = goal_documents
.iter()
.map(|doc| ChampionRow {
title: doc.metadata.title.clone(),
title: doc.metadata.title.to_string(),
url: format!(
"https://github.com/{org}/{repo}/blob/main/{path}",
org = repository.org,
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-project-goals-cli/src/rfc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ fn issue<'doc>(timeframe: &str, document: &'doc GoalDocument) -> Result<GithubIs
}

Ok(GithubIssue {
title: document.metadata.title.clone(),
title: document.metadata.title.to_string(),
assignees,
body: issue_text(timeframe, document)?,
labels,
Expand Down
4 changes: 4 additions & 0 deletions crates/rust-project-goals/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ pub struct TeamAskDetails {

/// Longer description
pub about: String,

/// If true, do not include in the RFC tables.
#[serde(default)]
pub elide: bool,
}

impl Configuration {
Expand Down
55 changes: 55 additions & 0 deletions crates/rust-project-goals/src/format_champions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::collections::{BTreeMap, BTreeSet};

use spanned::{Result, Spanned};

use crate::{goal::GoalDocument, util};

/// Format a champions table showing each champion and their goals.
pub fn format_champions(goals: &[&GoalDocument]) -> Result<String> {
use std::fmt::Write;

let mut output = String::new();

// Collect champions and their goals
let mut champion_goals: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();

for goal in goals {
for champion in goal.metadata.champions.values() {
let champion_name = champion.content.clone();
let goal_link = format!("° [{}]({})", goal.metadata.title.content, goal.link_path.display());

champion_goals
.entry(champion_name)
.or_default()
.insert(goal_link);
}
}

if champion_goals.is_empty() {
return Ok("No champions found.".to_string());
}

// Create the table
let table = {
let headings = vec![
Spanned::here("Champion".to_string()),
Spanned::here("#".to_string()),
Spanned::here("Goals".to_string()),
];

let rows = champion_goals.into_iter().map(|(champion, goals)| {
let goals_vec: Vec<String> = goals.into_iter().collect();
vec![
Spanned::here(champion),
Spanned::here(goals_vec.len().to_string()),
Spanned::here(goals_vec.join("<br>")),
]
});

std::iter::once(headings).chain(rows).collect::<Vec<_>>()
};

write!(output, "{}", util::format_table(&table))?;

Ok(output)
}
25 changes: 16 additions & 9 deletions crates/rust-project-goals/src/format_team_ask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ pub fn format_team_asks(asks_of_any_team: &[&TeamAsk]) -> Result<String> {
// the configuration. We prune out the ones that do not appear in the asks for a particular team.
let ask_headings = config
.team_asks
.keys()
.filter(|&ask_kind| {
asks_of_this_team
.iter()
.any(|a| &a.ask_description == ask_kind)
.iter()
.filter(|&(ask_kind, ask_details)| {
!ask_details.elide
&& asks_of_this_team
.iter()
.any(|a| &a.ask_description == ask_kind)
})
.map(|(ask_kind, _)| ask_kind)
.collect::<Vec<_>>();
let empty_row = || {
(0..ask_headings.len())
Expand All @@ -79,10 +81,15 @@ pub fn format_team_asks(asks_of_any_team: &[&TeamAsk]) -> Result<String> {

let row = goal_rows.entry(goal_data).or_insert_with(empty_row);

let index = ask_headings
.iter()
.position(|&h| h == &ask.ask_description)
.unwrap();
let Some(index) = ask_headings.iter().position(|&h| h == &ask.ask_description) else {
// Some asks are not included in the table
assert!(
config.team_asks[&ask.ask_description].elide,
"ask {} has no index but is not elided",
ask.ask_description
);
continue;
};

let text = if !ask.notes.is_empty() {
&ask.notes
Expand Down
59 changes: 31 additions & 28 deletions crates/rust-project-goals/src/goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub struct GoalDocument {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Metadata {
#[allow(unused)]
pub title: String,
pub title: Spanned<String>,
pub short_title: Spanned<String>,
pub pocs: String,
pub status: Spanned<Status>,
Expand Down Expand Up @@ -117,7 +117,7 @@ pub fn goals_in_dir(directory_path: &Path) -> Result<Vec<GoalDocument>> {
if path.file_name().unwrap() == "TEMPLATE.md" {
continue;
}

if let Some(goal_document) = GoalDocument::load(&path, &link_path)? {
goal_documents.push(goal_document);
}
Expand All @@ -137,11 +137,7 @@ impl GoalDocument {

let link_path = Arc::new(link_path.to_path_buf());

let goal_plans = if metadata.status.is_not_not_accepted() {
extract_plan_items(&sections)?
} else {
vec![]
};
let goal_plans = extract_plan_items(&sections)?;

let mut team_asks = vec![];
for goal_plan in &goal_plans {
Expand All @@ -156,7 +152,10 @@ impl GoalDocument {

// Enforce that every goal has some team asks (unless it is not accepted)
if metadata.status.is_not_not_accepted() && team_asks.is_empty() {
spanned::bail_here!("no team asks in goal; did you include `![Team]` in the table?");
spanned::bail!(
metadata.title,
"no team asks in goal; did you include `![Team]` in the table?"
);
}

let task_owners = goal_plans
Expand All @@ -168,7 +167,7 @@ impl GoalDocument {
Ok(Some(GoalDocument {
path: path.to_path_buf(),
link_path,
summary: summary.unwrap_or_else(|| metadata.title.clone()),
summary: summary.unwrap_or_else(|| (*metadata.title).clone()),
metadata,
team_asks,
goal_plans,
Expand Down Expand Up @@ -213,13 +212,14 @@ impl GoalDocument {

pub fn format_goal_table(goals: &[&GoalDocument]) -> Result<String> {
// If any of the goals have tracking issues, include those in the table.
let goals_are_proposed = goals
.iter()
.any(|g| g.metadata.status.acceptance == AcceptanceStatus::Proposed);
let show_champions = goals.iter().any(|g| {
g.metadata.status.acceptance == AcceptanceStatus::Proposed
|| g.metadata.status.acceptance == AcceptanceStatus::NotAccepted
});

let mut table;

if !goals_are_proposed {
if !show_champions {
table = vec![vec![
Spanned::here("Goal".to_string()),
Spanned::here("Point of contact".to_string()),
Expand Down Expand Up @@ -249,7 +249,7 @@ pub fn format_goal_table(goals: &[&GoalDocument]) -> Result<String> {
table.push(vec![
Spanned::here(format!(
"[{}]({})",
goal.metadata.title,
*goal.metadata.title,
goal.link_path.display()
)),
Spanned::here(goal.point_of_contact_for_goal_list()),
Expand All @@ -260,7 +260,7 @@ pub fn format_goal_table(goals: &[&GoalDocument]) -> Result<String> {
table = vec![vec![
Spanned::here("Goal".to_string()),
Spanned::here("Point of contact".to_string()),
Spanned::here("Team".to_string()),
Spanned::here("Team(s) and Champion(s)".to_string()),
]];

for goal in goals {
Expand All @@ -270,15 +270,27 @@ pub fn format_goal_table(goals: &[&GoalDocument]) -> Result<String> {
.flat_map(|ask| &ask.teams)
.copied()
.collect();
let teams: Vec<&TeamName> = teams.into_iter().collect();

// Format teams with champions in parentheses
let teams_with_champions: Vec<String> = teams
.into_iter()
.map(|team| {
if let Some(champion) = goal.metadata.champions.get(team) {
format!("{} ({})", team, champion.content)
} else {
team.to_string()
}
})
.collect();

table.push(vec![
Spanned::here(format!(
"[{}]({})",
goal.metadata.title,
*goal.metadata.title,
goal.link_path.display()
)),
Spanned::here(goal.point_of_contact_for_goal_list()),
Spanned::here(commas(&teams)),
Spanned::here(teams_with_champions.join(", ")),
]);
}
}
Expand Down Expand Up @@ -493,7 +505,7 @@ fn extract_metadata(sections: &[Section]) -> Result<Option<Metadata>> {
.map(|row| row[1].clone());

Ok(Some(Metadata {
title: title.to_string(),
title: title.clone(),
short_title: if let Some(row) = short_title_row {
row[1].clone()
} else {
Expand All @@ -508,8 +520,6 @@ fn extract_metadata(sections: &[Section]) -> Result<Option<Metadata>> {
}))
}



fn extract_summary(sections: &[Section]) -> Result<Option<String>> {
let Some(ownership_section) = sections.iter().find(|section| section.title == "Summary") else {
return Ok(None);
Expand Down Expand Up @@ -540,13 +550,6 @@ fn extract_plan_items<'i>(sections: &[Section]) -> Result<Vec<GoalPlan>> {
goal_plans.extend(goal_plan(Some(subsection.title.clone()), subsection)?);
}

if goal_plans.is_empty() {
spanned::bail!(
sections[ownership_index].title,
"no goal table items found in the `Ownership and team asks` section or subsections"
)
}

Ok(goal_plans)
}

Expand Down
1 change: 1 addition & 0 deletions crates/rust-project-goals/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod config;
pub mod format_team_ask;
pub mod format_champions;
pub mod gh;
pub mod goal;
pub mod markwaydown;
Expand Down
6 changes: 5 additions & 1 deletion crates/rust-project-goals/src/re.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ lazy_static! {
pub static ref TEAM_ASKS: Regex = Regex::new(r"\(\(\(TEAM ASKS\)\)\)").unwrap();
}

lazy_static! {
pub static ref CHAMPIONS: Regex = Regex::new(r"\(\(\(CHAMPIONS\)\)\)").unwrap();
}

// List of all goals, flagship or otherwise
lazy_static! {
pub static ref GOAL_LIST: Regex = Regex::new(r"\(\(\(GOALS\)\)\)").unwrap();
Expand Down Expand Up @@ -112,6 +116,6 @@ pub const TLDR: &str = "TL;DR:";
lazy_static! {
/// Metadata table rows like `[lang] champion` indicate the champion for the lang team
pub static ref CHAMPION_METADATA: Regex =
Regex::new(r"^\s*(?P<team>\[.*\]) champion)\s*$")
Regex::new(r"^\s*\[(?P<team>.*)\] champion\s*$")
.unwrap();
}
5 changes: 2 additions & 3 deletions rust-project-goals.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@

[team_asks]
"Allocate funds" = { short="Alloc funds", about="allocate funding" }
"Discussion and moral support" = { short="Good vibes", about="approve of this direction and be prepared for light discussion on Zulip or elsewhere" }
"Discussion and moral support" = { short="Good vibes", about="approve of this direction and be prepared for light discussion on Zulip or elsewhere", elide = true }
"Deploy to production" = { short="Deploy", about="deploy code to production (e.g., on crates.io" }
"Standard reviews" = { short="r?", about="review PRs (PRs are not expected to be unduly large or complicated)" }
"Standard reviews" = { short="r?", about="review PRs (PRs are not expected to be unduly large or complicated)", elide = true }
"Dedicated reviewer" = { short="Ded. r?", about="assign a specific person (or people) to review a series of PRs, appropriate for large or complex asks" }
"Lang-team champion" = { short="Champion", about="member of lang team or advisors who will champion the design within team" }
"Lang-team experiment" = { short="Experiment", about="begin a [lang-team experiment](https://lang-team.rust-lang.org/how_to/experiment.html) authorizing experimental impl of lang changes before an RFC is written; limited to trusted contributors" }
"Design meeting" = { short="Design mtg.", about="hold a synchronous meeting to review a proposal and provide feedback (no decision expected)" }
"RFC decision" = { short="RFC", about="review an RFC and deciding whether to accept" }
Expand Down
1 change: 0 additions & 1 deletion src/2025h1/GPU-Offload.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ Minimal "smoke test" reviews will be needed from the compiler-team. The Rust lan
|----------------------|------------------------|------------|
| Development | @ZuseZ4 | |
| Lang-team experiment | ![Team][] [lang][] | ![Complete][] |
| Lang-team champion | ![Team][] [lang][] | @traviscross |
| Standard reviews | ![Team][] [compiler][] | |

[Team]: https://img.shields.io/badge/Team%20ask-red
Expand Down
1 change: 0 additions & 1 deletion src/2025h1/arm-sve-sme.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ team in an RFC/FCP).
| ------------------------------------------------- | ------------------------- | ---------- |
| Extending type system to support scalable vectors | @davidtwco | |
| Author RFC | | |
| Lang-team champion | ![Team][] [lang] | @davidtwco |
| RFC decision | ![Team][] [types], [lang] | |
| Implementation | | |
| Standard reviews | ![Team][] [compiler] | |
Expand Down
Loading