66//! to the types in `gh` and so forth but because they represent
77//! a versioned API, we copy them over here to insulate them from incidental changes.
88
9- use std:: path:: PathBuf ;
9+ use std:: { path:: PathBuf , str :: FromStr } ;
1010
1111use serde:: Serialize ;
1212
1313use crate :: {
1414 gh:: {
1515 issue_id:: Repository ,
1616 issues:: {
17- list_issue_titles_in_milestone , ExistingGithubComment , ExistingGithubIssue ,
18- ExistingIssueState ,
17+ count_issues_matching_search , list_issue_titles_in_milestone , CountIssues ,
18+ ExistingGithubComment , ExistingGithubIssue , ExistingIssueState ,
1919 } ,
2020 } ,
2121 re,
@@ -32,13 +32,17 @@ pub(super) fn generate_json(
3232 issues : issues
3333 . into_iter ( )
3434 . map ( |( title, issue) | {
35- let ( total_checkboxes, checked_checkboxes) = checkboxes ( & issue) ;
35+ let progress = match checkboxes ( & issue) {
36+ Ok ( pair) => pair,
37+ Err ( e) => Progress :: Error {
38+ message : e. to_string ( ) ,
39+ } ,
40+ } ;
3641 TrackingIssue {
3742 number : issue. number ,
3843 title,
3944 flagship : is_flagship ( & issue) ,
40- total_checkboxes,
41- checked_checkboxes,
45+ progress,
4246 assignees : issue. assignees . into_iter ( ) . collect ( ) ,
4347 updates : updates ( issue. comments ) ,
4448 state : issue. state ,
@@ -80,11 +84,8 @@ struct TrackingIssue {
8084 /// True if this is a flagship goal
8185 flagship : bool ,
8286
83- /// Total checkboxes appearing in the body (i.e., `* [ ]` or `* [x]`)
84- total_checkboxes : u32 ,
85-
86- /// Checked checkboxes appearing in the body (i.e., `* [x]`)
87- checked_checkboxes : u32 ,
87+ /// State of progress
88+ progress : Progress ,
8889
8990 /// Set of assigned people
9091 assignees : Vec < String > ,
@@ -96,6 +97,23 @@ struct TrackingIssue {
9697 state : ExistingIssueState ,
9798}
9899
100+ #[ derive( Serialize ) ]
101+ enum Progress {
102+ /// We could not find any checkboxes or other deatils on the tracking issue.
103+ /// So all we have is "open" or "closed".
104+ Binary ,
105+
106+ /// We found checkboxes or issue listing.
107+ Tracked {
108+ completed : u32 ,
109+ total : u32 ,
110+ } ,
111+
112+ Error {
113+ message : String ,
114+ } ,
115+ }
116+
99117#[ derive( Serialize ) ]
100118struct TrackingIssueUpdate {
101119 pub author : String ,
@@ -105,21 +123,49 @@ struct TrackingIssueUpdate {
105123 pub url : String ,
106124}
107125
108- fn checkboxes ( issue : & ExistingGithubIssue ) -> ( u32 , u32 ) {
109- let mut total = 0 ;
110- let mut checked = 0 ;
126+ /// Identify how many sub-items have been completed.
127+ /// These can be encoded in two different ways:
128+ ///
129+ /// * Option A, the most common, is to have checkboxes in the issue. We just count the number that are checked.
130+ /// * Option B is to include a metadata line called "Tracked issues" that lists a search query. We count the number of open vs closed issues in that query.
131+ ///
132+ /// Returns a tuple (completed, total) with the number of completed items and the total number of items.
133+ fn checkboxes ( issue : & ExistingGithubIssue ) -> anyhow:: Result < Progress > {
134+ let mut checkboxes = None ;
135+ let mut issues = None ;
111136
112137 for line in issue. body . lines ( ) {
113- if re:: CHECKBOX . is_match ( line) {
114- total += 1 ;
138+ // Does this match TRACKED_ISSUES?
139+ if let Some ( c) = re:: TRACKED_ISSUES_QUERY . captures ( line) {
140+ let repo = Repository :: from_str ( & c[ 1 ] ) ?;
141+ let query = & c[ 2 ] ;
142+
143+ if issues. is_some ( ) {
144+ anyhow:: bail!( "found multiple search queries for Tracked Issues" ) ;
145+ }
146+
147+ let CountIssues { open, closed } = count_issues_matching_search ( & repo, query) ?;
148+ issues = Some ( ( closed, open + closed) ) ;
115149 }
116150
151+ let ( checked, total) = checkboxes. unwrap_or ( ( 0 , 0 ) ) ;
152+
117153 if re:: CHECKED_CHECKBOX . is_match ( line) {
118- checked += 1 ;
154+ checkboxes = Some ( ( checked + 1 , total + 1 ) ) ;
155+ } else if re:: CHECKBOX . is_match ( line) {
156+ checkboxes = Some ( ( checked, total + 1 ) ) ;
119157 }
120158 }
121159
122- ( total, checked)
160+ eprintln ! ( "#{}: {checkboxes:?}, {issues:?}" , issue. number) ;
161+
162+ match ( checkboxes, issues) {
163+ ( Some ( ( completed, total) ) , None ) | ( None , Some ( ( completed, total) ) ) => {
164+ Ok ( Progress :: Tracked { completed, total } )
165+ }
166+ ( None , None ) => Ok ( Progress :: Binary ) ,
167+ ( Some ( _) , Some ( _) ) => anyhow:: bail!( "found both Tracked Issues and checkboxes" ) ,
168+ }
123169}
124170
125171fn is_flagship ( issue : & ExistingGithubIssue ) -> bool {
0 commit comments