Skip to content

Commit 72dde22

Browse files
authored
Merge pull request #192 from nikomatsakis/task-subsections
Subgoals via subsections
2 parents 7af4b89 + 1290374 commit 72dde22

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+749
-572
lines changed

crates/rust-project-goals-cli/src/rfc.rs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rust_project_goals::{
1818
},
1919
labels::GhLabel,
2020
},
21-
goal::{self, GoalDocument, ParsedOwners, PlanItem, Status},
21+
goal::{self, GoalDocument, GoalPlan, ParsedOwners, Status},
2222
team::{get_person_data, TeamName},
2323
};
2424

@@ -324,8 +324,8 @@ fn issue<'doc>(timeframe: &str, document: &'doc GoalDocument) -> anyhow::Result<
324324

325325
fn issue_text(timeframe: &str, document: &GoalDocument) -> anyhow::Result<String> {
326326
let mut tasks = vec![];
327-
for plan_item in &document.plan_items {
328-
tasks.extend(task_items(plan_item)?);
327+
for goal_plan in &document.goal_plans {
328+
tasks.extend(task_items(goal_plan)?);
329329
}
330330

331331
let teams = document
@@ -361,35 +361,38 @@ fn issue_text(timeframe: &str, document: &GoalDocument) -> anyhow::Result<String
361361
))
362362
}
363363

364-
fn task_items(plan_item: &PlanItem) -> anyhow::Result<Vec<String>> {
364+
fn task_items(goal_plan: &GoalPlan) -> anyhow::Result<Vec<String>> {
365365
use std::fmt::Write;
366366

367+
367368
let mut tasks = vec![];
368369

369-
let mut description = format!(
370-
"* {box} {text}",
371-
box = if plan_item.is_complete() { "[x]" } else { "[ ]" },
372-
text = plan_item.text
373-
);
370+
if let Some(title) = &goal_plan.subgoal {
371+
tasks.push(format!("### {title}"));
372+
}
374373

375-
if let Some(parsed_owners) = plan_item.parse_owners()? {
376-
match parsed_owners {
377-
ParsedOwners::TeamAsks(asks) => {
378-
let teams: Vec<String> = asks.iter().map(|ask| ask.name_and_link()).collect();
374+
for plan_item in &goal_plan.plan_items {
375+
let mut description = format!(
376+
"* {box} {text}",
377+
box = if plan_item.is_complete() { "[x]" } else { "[ ]" },
378+
text = plan_item.text
379+
);
379380

380-
write!(description, " ({} ![Team][])", teams.join(", "))?;
381-
}
381+
if let Some(parsed_owners) = plan_item.parse_owners()? {
382+
match parsed_owners {
383+
ParsedOwners::TeamAsks(asks) => {
384+
let teams: Vec<String> = asks.iter().map(|ask| ask.name_and_link()).collect();
382385

383-
ParsedOwners::Usernames(usernames) => {
384-
write!(description, " ({})", usernames.join(", "))?;
386+
write!(description, " ({} ![Team][])", teams.join(", "))?;
387+
}
388+
389+
ParsedOwners::Usernames(usernames) => {
390+
write!(description, " ({})", usernames.join(", "))?;
391+
}
385392
}
386393
}
387-
}
388-
389-
tasks.push(description);
390394

391-
for task in &plan_item.children {
392-
tasks.extend(task_items(task)?.into_iter().map(|t| format!(" {}", &t)));
395+
tasks.push(description);
393396
}
394397

395398
Ok(tasks)

crates/rust-project-goals/src/goal.rs

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct GoalDocument {
3030
pub summary: String,
3131

3232
/// The "plan" for completing the goal (includes things owners will do as well as team asks)
33-
pub plan_items: Vec<PlanItem>,
33+
pub goal_plans: Vec<GoalPlan>,
3434

3535
/// List of team asks extracted from the goal
3636
pub team_asks: Vec<TeamAsk>,
@@ -50,13 +50,22 @@ pub struct Metadata {
5050

5151
pub const TRACKING_ISSUE_ROW: &str = "Tracking issue";
5252

53+
/// Items required to complete the goal.
54+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
55+
pub struct GoalPlan {
56+
/// If `Some`, title of the subsection in which these items were found.
57+
pub subgoal: Option<String>,
58+
59+
/// List of items found in the table.
60+
pub plan_items: Vec<PlanItem>,
61+
}
62+
5363
/// Identifies a particular ask for a set of Rust teams
5464
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
5565
pub struct PlanItem {
5666
pub text: String,
5767
pub owners: String,
5868
pub notes: String,
59-
pub children: Vec<PlanItem>,
6069
}
6170

6271
/// Returns the "owner(s)" of a plan-item, which can be
@@ -116,20 +125,23 @@ impl GoalDocument {
116125

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

119-
let plan_items = match metadata.status {
128+
let goal_plans = match metadata.status {
120129
Status::Flagship | Status::Accepted | Status::Proposed => {
121130
extract_plan_items(&sections)?
122131
}
123132
Status::NotAccepted => vec![],
124133
};
125134

126135
let mut team_asks = vec![];
127-
for plan_item in &plan_items {
128-
team_asks.extend(plan_item.team_asks(
129-
&link_path,
130-
&metadata.short_title,
131-
&metadata.owners,
132-
)?);
136+
for goal_plan in &goal_plans {
137+
let goal_title = goal_plan.subgoal.as_deref().unwrap_or(&metadata.short_title);
138+
for plan_item in &goal_plan.plan_items {
139+
team_asks.extend(plan_item.team_asks(
140+
&link_path,
141+
goal_title,
142+
&metadata.owners,
143+
)?);
144+
}
133145
}
134146

135147
Ok(Some(GoalDocument {
@@ -138,7 +150,7 @@ impl GoalDocument {
138150
summary: summary.unwrap_or_else(|| metadata.title.clone()),
139151
metadata,
140152
team_asks,
141-
plan_items,
153+
goal_plans,
142154
}))
143155
}
144156

@@ -201,7 +213,7 @@ pub fn format_team_asks(asks_of_any_team: &[&TeamAsk]) -> anyhow::Result<String>
201213

202214
for subgoal in subgoals {
203215
table.push(vec![
204-
format!("*{}*", subgoal),
216+
format!("**{}**", subgoal),
205217
"".to_string(),
206218
"".to_string(),
207219
]);
@@ -390,29 +402,52 @@ fn extract_summary(sections: &[Section]) -> anyhow::Result<Option<String>> {
390402
Ok(Some(ownership_section.text.trim().to_string()))
391403
}
392404

393-
fn extract_plan_items<'i>(sections: &[Section]) -> anyhow::Result<Vec<PlanItem>> {
394-
let Some(ownership_section) = sections
405+
fn extract_plan_items<'i>(sections: &[Section]) -> anyhow::Result<Vec<GoalPlan>> {
406+
let Some(ownership_index) = sections
395407
.iter()
396-
.find(|section| section.title == "Ownership and team asks")
408+
.position(|section| section.title == "Ownership and team asks")
397409
else {
398410
anyhow::bail!("no `Ownership and team asks` section found")
399411
};
400412

401-
let Some(table) = ownership_section.tables.first() else {
402-
anyhow::bail!(
403-
"on line {}, no table found in `Ownership and team asks` section",
404-
ownership_section.line_num
405-
)
406-
};
413+
// Extract the plan items from the main section (if any)
414+
let level= sections[ownership_index].level;
415+
416+
let mut goal_plans = vec![];
417+
goal_plans.extend(goal_plan(None, &sections[ownership_index])?);
418+
419+
for subsection in sections.iter().skip(ownership_index + 1).take_while(|s| s.level > level) {
420+
goal_plans.extend(goal_plan(Some(subsection.title.clone()), subsection)?);
421+
}
407422

408-
expect_headers(table, &["Subgoal", "Owner(s) or team(s)", "Notes"])?;
423+
if goal_plans.is_empty() {
424+
anyhow::bail!("no goal table items found in the `Ownership and team asks` section or subsections")
425+
}
426+
427+
Ok(goal_plans)
428+
}
409429

410-
let mut rows = table.rows.iter().peekable();
411-
let mut plan_items = vec![];
412-
while rows.peek().is_some() {
413-
plan_items.push(extract_plan_item(&mut rows)?);
430+
fn goal_plan(subgoal: Option<String>, section: &Section) -> anyhow::Result<Option<GoalPlan>> {
431+
match section.tables.len() {
432+
0 => Ok(None),
433+
1 => {
434+
let table = &section.tables[0];
435+
expect_headers(table, &["Task", "Owner(s) or team(s)", "Notes"])?;
436+
437+
let mut rows = table.rows.iter().peekable();
438+
let mut plan_items = vec![];
439+
while rows.peek().is_some() {
440+
plan_items.push(extract_plan_item(&mut rows)?);
441+
}
442+
443+
Ok(Some(GoalPlan {
444+
subgoal,
445+
plan_items,
446+
}))
447+
}
448+
_ => anyhow::bail!("multiple goal tables found in section `{}`", section.title),
414449
}
415-
Ok(plan_items)
450+
416451
}
417452

418453
fn extract_plan_item(
@@ -422,33 +457,11 @@ fn extract_plan_item(
422457
anyhow::bail!("unexpected end of table");
423458
};
424459

425-
let mut subgoal = row[0].trim();
426-
let mut is_child = false;
427-
428-
if subgoal.starts_with(ARROW) {
429-
// e.g., "↳ stabilization" is a subtask of the metagoal
430-
subgoal = row[0][ARROW.len()..].trim();
431-
is_child = true;
432-
}
433-
434-
let mut item = PlanItem {
435-
text: subgoal.to_string(),
460+
Ok(PlanItem {
461+
text: row[0].to_string(),
436462
owners: row[1].to_string(),
437463
notes: row[2].to_string(),
438-
children: vec![],
439-
};
440-
441-
if !is_child {
442-
while let Some(row) = rows.peek() {
443-
if !row[0].starts_with(ARROW) {
444-
break;
445-
}
446-
447-
item.children.push(extract_plan_item(rows)?);
448-
}
449-
}
450-
451-
Ok(item)
464+
})
452465
}
453466

454467
impl PlanItem {
@@ -543,16 +556,6 @@ impl PlanItem {
543556
});
544557
}
545558

546-
for child in &self.children {
547-
// If this item has owners listed, they take precedence, otherwise use the owners in scope.
548-
let owners = if self.owners.is_empty() {
549-
goal_owners
550-
} else {
551-
&self.owners
552-
};
553-
asks.extend(child.team_asks(link_path, &self.text, owners)?);
554-
}
555-
556559
Ok(asks)
557560
}
558561
}

crates/rust-project-goals/src/markwaydown.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,23 @@ use std::{fmt::Display, path::Path};
44

55
use crate::util;
66

7+
/// A "section" is a piece of markdown that begins with `##` and which extends until the next section.
8+
/// Note that we don't track the hierarchical structure of sections in particular.
79
#[derive(Debug)]
810
pub struct Section {
11+
/// Line numberin the document
912
pub line_num: usize,
13+
14+
/// Number of hashes
15+
pub level: usize,
16+
17+
/// Title of the section -- what came after the `#` in the markdown.
1018
pub title: String,
19+
20+
/// Markdown text until start of next section, excluding tables
1121
pub text: String,
22+
23+
/// Tables are parsed and stored here
1224
pub tables: Vec<Table>,
1325
}
1426

@@ -30,10 +42,11 @@ pub fn parse(path: &Path) -> anyhow::Result<Vec<Section>> {
3042
// eprintln!("line = {:?}", line);
3143
// eprintln!("categorized = {:?}", categorized);
3244
match categorized {
33-
CategorizeLine::Title(title) => {
45+
CategorizeLine::Title(level, title) => {
3446
close_section(&mut result, &mut open_section, &mut open_table);
3547
open_section = Some(Section {
3648
line_num,
49+
level,
3750
title,
3851
text: String::new(),
3952
tables: vec![],
@@ -131,15 +144,16 @@ fn close_section(
131144

132145
#[derive(Debug)]
133146
enum CategorizeLine {
134-
Title(String),
147+
Title(usize, String),
135148
TableRow(Vec<String>),
136149
TableDashRow(usize),
137150
Other,
138151
}
139152

140153
fn categorize_line(line: &str) -> CategorizeLine {
141154
if line.starts_with('#') {
142-
CategorizeLine::Title(line.trim_start_matches('#').trim().to_string())
155+
let level = line.chars().take_while(|&ch| ch == '#').count();
156+
CategorizeLine::Title(level, line.trim_start_matches('#').trim().to_string())
143157
} else if line.starts_with('|') && line.ends_with('|') {
144158
let line = &line[1..line.len() - 1];
145159
let columns = line.split('|').map(|s| s.trim());

src/2024h2/ATPIT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ None.
6060

6161
**Owner:** oli-obk owns this goal.
6262

63-
| Subgoal | Owner(s) or team(s) | Notes |
63+
| Task | Owner(s) or team(s) | Notes |
6464
|------------------------|--------------------------|-------|
6565
| Implementation | @oli-obk | |
6666
| FCP decision(s) | ![Team][] [types] | |

src/2024h2/Patterns-of-empty-types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ already for several months.
9595
the compiler team, but not more than the ordinary.
9696

9797

98-
| Subgoal | Owner(s) or team(s) | Notes |
98+
| Task | Owner(s) or team(s) | Notes |
9999
| ---------------------------- | -------------------- | ----- |
100100
| Author RFC | @Nadrieril | |
101101
| Implementation | @Nadrieril | |

src/2024h2/Polonius.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Other support provided by @amandasystems as part of her PhD.
4444

4545
[amanda]: https://github.com/amandasystems
4646

47-
| Subgoal | Owner(s) or team(s) | Notes |
47+
| Task | Owner(s) or team(s) | Notes |
4848
| ---------------- | -------------------- | -------------- |
4949
| Design review | @nikomatsakis | |
5050
| Implementation | @lqd, @amandasystems | |

src/2024h2/Project-goal-slate.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ More concretely, assuming this goal program is successful, we would like to begi
7777

7878
* @nikomatsakis can commit 20% time (avg of 1 days per week) to pursue this task, which he estimates to be sufficient.
7979

80-
| Subgoal | Owner(s) or team(s) | Notes |
80+
| Task | Owner(s) or team(s) | Notes |
8181
| ------------------------------------------ | ------------------------------ | ----------------------------------------------------------------------------------------- |
8282
| RFC decision | ![Team][] [leadership-council] | ![Complete][] |
8383
| Inside Rust blog post inviting feedback | | [Posted](https://blog.rust-lang.org/inside-rust/2024/05/07/announcing-project-goals.html) |

src/2024h2/Relaxing-the-Orphan-Rule.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ This section defines the specific work items that are planned and who is expecte
119119
* List ![Help wanted][] if there is an owner but they need support, for example funding.
120120
* Other needs (e.g., complete, in FCP, etc) are also fine.
121121

122-
| Subgoal | Owner(s) or team(s) | Notes |
122+
| Task | Owner(s) or team(s) | Notes |
123123
| ----------------------------- | ------------------------ | -------------------------- |
124124
| Ownership and implementation | ![Help wanted][] | |
125125
| RFC authoring | ![Help wanted][] | |

src/2024h2/Rust-2024-Edition.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ The "Edition Axioms" were [laid out in RFC #3085](https://rust-lang.github.io/rf
5151

5252
**Owner:** @traviscross
5353

54-
| Subgoal | Owner(s) or team(s) | Notes |
54+
| Task | Owner(s) or team(s) | Notes |
5555
| ------------------------ | ------------------------------ | --------------------------- |
5656
| RFC decision | ![Team][] [leadership-council] | ![Complete][] ([RFC #3501]) |
5757
| Stabilization decision | ![Team][] [lang] [types] | |

0 commit comments

Comments
 (0)