@@ -21,8 +21,8 @@ pub struct Taskwarrior {
2121 update_interval : Duration ,
2222 warning_threshold : u32 ,
2323 critical_threshold : u32 ,
24- filter_tags : Vec < String > ,
25- block_mode : TaskwarriorBlockMode ,
24+ filters : Vec < Filter > ,
25+ filter_index : usize ,
2626 format : FormatTemplate ,
2727 format_singular : FormatTemplate ,
2828 format_everything_done : FormatTemplate ,
@@ -34,6 +34,29 @@ pub struct Taskwarrior {
3434 tx_update_request : Sender < Task > ,
3535}
3636
37+ #[ derive( Deserialize , Debug , Default , Clone ) ]
38+ #[ serde( deny_unknown_fields) ]
39+ pub struct Filter {
40+ pub name : String ,
41+ pub filter : String ,
42+ }
43+
44+ impl Filter {
45+ pub fn new ( name : String , filter : String ) -> Self {
46+ Filter { name, filter }
47+ }
48+
49+ pub fn legacy ( name : String , tags : & [ String ] ) -> Self {
50+ let tags = tags
51+ . iter ( )
52+ . map ( |element| format ! ( "+{}" , element) )
53+ . collect :: < Vec < String > > ( )
54+ . join ( " " ) ;
55+ let filter = format ! ( "-COMPLETED -DELETED {}" , tags) ;
56+ Self :: new ( name, filter)
57+ }
58+ }
59+
3760#[ derive( Deserialize , Debug , Default , Clone ) ]
3861#[ serde( deny_unknown_fields) ]
3962pub struct TaskwarriorConfig {
@@ -53,32 +76,31 @@ pub struct TaskwarriorConfig {
5376 pub critical_threshold : u32 ,
5477
5578 /// A list of tags a task has to have before it's used for counting pending tasks
79+ /// (DEPRECATED) use filters instead
5680 #[ serde( default = "TaskwarriorConfig::default_filter_tags" ) ]
5781 pub filter_tags : Vec < String > ,
5882
83+ /// A list of named filter criteria which must be fulfilled to be counted towards
84+ /// the total, when that filter is active.
85+ #[ serde( default = "TaskwarriorConfig::default_filters" ) ]
86+ pub filters : Vec < Filter > ,
87+
5988 /// Format override
6089 #[ serde( default = "TaskwarriorConfig::default_format" ) ]
6190 pub format : String ,
6291
63- /// Format override if exactly one task is pending
92+ /// Format override if the count is one
6493 #[ serde( default = "TaskwarriorConfig::default_format" ) ]
6594 pub format_singular : String ,
6695
67- /// Format override if all tasks are completed
96+ /// Format override if the count is zero
6897 #[ serde( default = "TaskwarriorConfig::default_format" ) ]
6998 pub format_everything_done : String ,
7099
71100 #[ serde( default = "TaskwarriorConfig::default_color_overrides" ) ]
72101 pub color_overrides : Option < BTreeMap < String , String > > ,
73102}
74103
75- enum TaskwarriorBlockMode {
76- // Show only the tasks which are filtered by the set tags and which are not completed.
77- OnlyFilteredPendingTasks ,
78- // Show all pending tasks and ignore the filtering tags.
79- AllPendingTasks ,
80- }
81-
82104impl TaskwarriorConfig {
83105 fn default_interval ( ) -> Duration {
84106 Duration :: from_secs ( 600 )
@@ -96,6 +118,13 @@ impl TaskwarriorConfig {
96118 vec ! [ ]
97119 }
98120
121+ fn default_filters ( ) -> Vec < Filter > {
122+ vec ! [ Filter :: new(
123+ "pending" . to_string( ) ,
124+ "-COMPLETED -DELETED" . to_string( ) ,
125+ ) ]
126+ }
127+
99128 fn default_format ( ) -> String {
100129 "{count}" . to_owned ( )
101130 }
@@ -117,15 +146,22 @@ impl ConfigBlock for Taskwarrior {
117146 let output = ButtonWidget :: new ( config. clone ( ) , & id)
118147 . with_icon ( "tasks" )
119148 . with_text ( "-" ) ;
149+ // If the deprecated `filter_tags` option has been set,
150+ // convert it to the new `filter` format.
151+ let filters = if block_config. filter_tags . len ( ) > 0 {
152+ vec ! [
153+ Filter :: legacy( "filtered" . to_string( ) , & block_config. filter_tags) ,
154+ Filter :: legacy( "all" . to_string( ) , & vec![ ] ) ,
155+ ]
156+ } else {
157+ block_config. filters
158+ } ;
120159
121160 Ok ( Taskwarrior {
122161 id : pseudo_uuid ( ) ,
123162 update_interval : block_config. interval ,
124163 warning_threshold : block_config. warning_threshold ,
125164 critical_threshold : block_config. critical_threshold ,
126- filter_tags : block_config. filter_tags ,
127- block_mode : TaskwarriorBlockMode :: OnlyFilteredPendingTasks ,
128- output,
129165 format : FormatTemplate :: from_string ( & block_config. format ) . block_error (
130166 "taskwarrior" ,
131167 "Invalid format specified for taskwarrior::format" ,
@@ -143,7 +179,10 @@ impl ConfigBlock for Taskwarrior {
143179 "Invalid format specified for taskwarrior::format_everything_done" ,
144180 ) ?,
145181 tx_update_request,
182+ filter_index : 0 ,
146183 config,
184+ filters,
185+ output,
147186 } )
148187 }
149188}
@@ -164,33 +203,20 @@ fn has_taskwarrior() -> Result<bool> {
164203 != "" )
165204}
166205
167- fn tags_to_filter ( tags : & [ String ] ) -> String {
168- tags. iter ( )
169- . map ( |element| format ! ( "+{}" , element) )
170- . collect :: < Vec < String > > ( )
171- . join ( " " )
172- }
173-
174- fn get_number_of_pending_tasks ( tags : & [ String ] ) -> Result < u32 > {
206+ fn get_number_of_tasks ( filter : & str ) -> Result < u32 > {
175207 String :: from_utf8 (
176208 Command :: new ( "sh" )
177- . args ( & [
178- "-c" ,
179- & format ! (
180- "task rc.gc=off -COMPLETED -DELETED {} count" ,
181- tags_to_filter( tags)
182- ) ,
183- ] )
209+ . args ( & [ "-c" , & format ! ( "task rc.gc=off {} count" , filter) ] )
184210 . output ( )
185211 . block_error (
186212 "taskwarrior" ,
187- "failed to run taskwarrior for getting the number of pending tasks" ,
213+ "failed to run taskwarrior for getting the number of tasks" ,
188214 ) ?
189215 . stdout ,
190216 )
191217 . block_error (
192218 "taskwarrior" ,
193- "failed to get the number of pending tasks from taskwarrior" ,
219+ "failed to get the number of tasks from taskwarrior" ,
194220 ) ?
195221 . trim ( )
196222 . parse :: < u32 > ( )
@@ -202,20 +228,23 @@ impl Block for Taskwarrior {
202228 if !has_taskwarrior ( ) ? {
203229 self . output . set_text ( "?" )
204230 } else {
205- let filter_tags = match self . block_mode {
206- TaskwarriorBlockMode :: OnlyFilteredPendingTasks => self . filter_tags . clone ( ) ,
207- TaskwarriorBlockMode :: AllPendingTasks => vec ! [ ] ,
208- } ;
209- let number_of_pending_tasks = get_number_of_pending_tasks ( & filter_tags) ?;
210- let values = map ! ( "{count}" => number_of_pending_tasks) ;
211- self . output . set_text ( match number_of_pending_tasks {
231+ let filter = self . filters . get ( self . filter_index ) . block_error (
232+ "taskwarrior" ,
233+ & format ! ( "Filter at index {} does not exist" , self . filter_index) ,
234+ ) ?;
235+ let number_of_tasks = get_number_of_tasks ( & filter. filter ) ?;
236+ let values = map ! (
237+ "{count}" => number_of_tasks. to_string( ) ,
238+ "{filter_name}" => filter. name. clone( )
239+ ) ;
240+ self . output . set_text ( match number_of_tasks {
212241 0 => self . format_everything_done . render_static_str ( & values) ?,
213242 1 => self . format_singular . render_static_str ( & values) ?,
214243 _ => self . format . render_static_str ( & values) ?,
215244 } ) ;
216- if number_of_pending_tasks >= self . critical_threshold {
245+ if number_of_tasks >= self . critical_threshold {
217246 self . output . set_state ( State :: Critical ) ;
218- } else if number_of_pending_tasks >= self . warning_threshold {
247+ } else if number_of_tasks >= self . warning_threshold {
219248 self . output . set_state ( State :: Warning ) ;
220249 } else {
221250 self . output . set_state ( State :: Idle ) ;
@@ -237,14 +266,8 @@ impl Block for Taskwarrior {
237266 self . update ( ) ?;
238267 }
239268 MouseButton :: Right => {
240- match self . block_mode {
241- TaskwarriorBlockMode :: OnlyFilteredPendingTasks => {
242- self . block_mode = TaskwarriorBlockMode :: AllPendingTasks
243- }
244- TaskwarriorBlockMode :: AllPendingTasks => {
245- self . block_mode = TaskwarriorBlockMode :: OnlyFilteredPendingTasks
246- }
247- }
269+ // Increment the filter_index, rotating at the end
270+ self . filter_index = ( self . filter_index + 1 ) % self . filters . len ( ) ;
248271 self . update ( ) ?;
249272 }
250273 _ => { }
0 commit comments