Skip to content
Merged
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
58 changes: 34 additions & 24 deletions crates/rust-project-goals/src/goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1152,28 +1152,24 @@ pub fn format_highlight_goal_sections(
}

/// Format a summary table of goals needing funding.
/// Columns: status emoji, Goal, Cost, Sponsor.
/// Columns: status emoji, Goal, Cost, Funding contact, Sponsor(s).
pub fn format_funding_table(goals: &[&GoalDocument]) -> String {
let mut output = String::new();
output.push_str("| | Goal | Cost | Funding contact | Sponsor(s) |\n");
output.push_str("| --- | --- | --- | --- | --- |\n");
format_funding_table_header(&mut output, true);
for goal in goals {
format_funding_table_row(&mut output, goal);
format_funding_table_row(&mut output, goal, true);
}
output
}

/// Format a summary table of goals needing funding, grouped by roadmap.
/// Each roadmap gets a bold header row linking to the roadmap page.
/// Each roadmap gets a heading linking to the roadmap page and its own table.
/// Goals not in any roadmap go under "Other goals".
pub fn format_funding_table_grouped(
goals: &[&GoalDocument],
roadmaps: &[&RoadmapDocument],
) -> String {
let mut output = String::new();
output.push_str("| | Goal | Cost | Funding contact | Sponsor(s) |\n");
output.push_str("| --- | --- | --- | --- | --- |\n");

let mut used: Vec<bool> = vec![false; goals.len()];

// Sort roadmaps alphabetically by short title
Expand All @@ -1193,37 +1189,37 @@ pub fn format_funding_table_grouped(
continue;
}

// Emit roadmap header row
output.push_str(&format!(
"| - | **[{}]({})** | -- | -- | -- |\n",
theme,
roadmap.link_path.display(),
"### [{theme}]({url})\n\n",
url = roadmap.link_path.display(),
));
format_funding_table_header(&mut output, true);

for i in &matching {
used[*i] = true;
format_funding_table_row(&mut output, goals[*i]);
format_funding_table_row(&mut output, goals[*i], true);
}
output.push('\n');
}

// Emit "Other goals" for anything not in a roadmap
let other: Vec<usize> = (0..goals.len()).filter(|i| !used[*i]).collect();
if !other.is_empty() {
output.push_str("| - | **Other goals** | -- | -- | -- |\n");
output.push_str("### Other goals\n\n");
format_funding_table_header(&mut output, true);
for i in &other {
format_funding_table_row(&mut output, goals[*i]);
format_funding_table_row(&mut output, goals[*i], true);
}
output.push('\n');
}

output
}

/// Format a summary table of goals needing funding, grouped by funding contact.
/// Each distinct contact gets a bold header row. Goals are listed underneath their contact.
/// Each distinct contact gets a heading and its own table (without the contact column).
pub fn format_funding_table_grouped_by_contact(goals: &[&GoalDocument]) -> String {
let mut output = String::new();
output.push_str("| | Goal | Cost | Funding contact | Sponsor(s) |\n");
output.push_str("| --- | --- | --- | --- | --- |\n");

// Group goals by their funding contact
let mut by_contact: BTreeMap<String, Vec<&GoalDocument>> = BTreeMap::new();
Expand All @@ -1233,11 +1229,13 @@ pub fn format_funding_table_grouped_by_contact(goals: &[&GoalDocument]) -> Strin
}

for (contact, contact_goals) in &by_contact {

@lcnr lcnr Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about sorting contacts by number of goals?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should maybe remove that table altogether. Is it useful to anyone to know which contact has which goals?

In any case, I think this table should be grouped alphabetically (which it's not currently).

output.push_str(&format!("| - | **{}** | -- | -- | -- |\n", contact));
output.push_str(&format!("### {contact}\n\n"));
format_funding_table_header(&mut output, false);

for goal in contact_goals {
format_funding_table_row(&mut output, goal);
format_funding_table_row(&mut output, goal, false);
}
output.push('\n');
}

output
Expand All @@ -1252,7 +1250,17 @@ pub fn format_funding_legend() -> String {
)
}

fn format_funding_table_row(output: &mut String, goal: &GoalDocument) {
fn format_funding_table_header(output: &mut String, include_contact: bool) {
if include_contact {
output.push_str("| | Goal | Cost | Funding contact | Sponsor(s) |\n");
output.push_str("| --- | --- | --- | --- | --- |\n");
} else {
output.push_str("| | Goal | Cost | Sponsor(s) |\n");
output.push_str("| --- | --- | --- | --- |\n");
}
}

fn format_funding_table_row(output: &mut String, goal: &GoalDocument, include_contact: bool) {
let total = sum_funding_costs(
&goal
.funding
Expand All @@ -1263,14 +1271,16 @@ fn format_funding_table_row(output: &mut String, goal: &GoalDocument) {
let cost_str = total.map(|c| c.display()).unwrap_or_default();
let status = goal.funding_status();
output.push_str(&format!(
"| {} | [{}]({}#funding) | {} | {} | {} |\n",
"| {} | [{}]({}#funding) | {} |",
status.emoji(),
goal.title_with_status(),
goal.link_path.display(),
cost_str,
goal.funding_contact(),
goal.sponsors_display(),
));
if include_contact {
output.push_str(&format!(" {} |", goal.funding_contact()));
}
output.push_str(&format!(" {} |\n", goal.sponsors_display()));
}

/// Format goals that need funding as sections with people, summary, and funding table.
Expand Down
Loading