@@ -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
5151pub 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 ) ]
5565pub 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
418453fn 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
454467impl 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}
0 commit comments