1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // https://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26 
27 #include <cmake.h>
28 #include <CmdIDs.h>
29 #include <sstream>
30 #include <algorithm>
31 #include <Context.h>
32 #include <Filter.h>
33 #include <main.h>
34 #include <shared.h>
35 
36 std::string zshColonReplacement = ",";
37 
38 ////////////////////////////////////////////////////////////////////////////////
CmdIDs()39 CmdIDs::CmdIDs ()
40 {
41   _keyword               = "ids";
42   _usage                 = "task <filter> ids";
43   _description           = "Shows the IDs of matching tasks, as a range";
44   _read_only             = true;
45   _displays_id           = true;
46   _needs_gc              = true;
47   _uses_context          = false;
48   _accepts_filter        = true;
49   _accepts_modifications = false;
50   _accepts_miscellaneous = false;
51   _category              = Command::Category::metadata;
52 }
53 
54 ////////////////////////////////////////////////////////////////////////////////
execute(std::string & output)55 int CmdIDs::execute (std::string& output)
56 {
57   // Apply filter.
58   handleUntil ();
59   handleRecurrence ();
60   Filter filter;
61   std::vector <Task> filtered;
62   filter.subset (filtered);
63 
64   // Find number of matching tasks.
65   std::vector <int> ids;
66   for (auto& task : filtered)
67     if (task.id)
68       ids.push_back (task.id);
69 
70   std::sort (ids.begin (), ids.end ());
71   output = compressIds (ids) + '\n';
72 
73   Context::getContext ().headers.clear ();
74   return 0;
75 }
76 
77 ////////////////////////////////////////////////////////////////////////////////
78 // The vector must be sorted first.  This is a modified version of the run-
79 // length encoding algorithm.
80 //
81 // This function converts the vector:
82 //
83 //   [1, 3, 4, 6, 7, 8, 9, 11]
84 //
85 // to ths string:
86 //
87 //   1,3-4,6-9,11
88 //
compressIds(const std::vector<int> & ids)89 std::string CmdIDs::compressIds (const std::vector <int>& ids)
90 {
91   std::stringstream result;
92 
93   auto range_start = 0;
94   auto range_end = 0;
95 
96   for (unsigned int i = 0; i < ids.size (); ++i)
97   {
98     if (i + 1 == ids.size ())
99     {
100       if (result.str ().length ())
101         result << ' ';
102 
103       if (range_start < range_end)
104         result << ids[range_start] << '-' << ids[range_end];
105       else
106         result << ids[range_start];
107     }
108     else
109     {
110       if (ids[range_end] + 1 == ids[i + 1])
111       {
112         ++range_end;
113       }
114       else
115       {
116         if (result.str ().length ())
117           result << ' ';
118 
119         if (range_start < range_end)
120           result << ids[range_start] << '-' << ids[range_end];
121         else
122           result << ids[range_start];
123 
124         range_start = range_end = i + 1;
125       }
126     }
127   }
128 
129   return result.str ();
130 }
131 
132 ////////////////////////////////////////////////////////////////////////////////
CmdCompletionIds()133 CmdCompletionIds::CmdCompletionIds ()
134 {
135   _keyword               = "_ids";
136   _usage                 = "task <filter> _ids";
137   _description           = "Shows the IDs of matching tasks, in the form of a list";
138   _read_only             = true;
139   _displays_id           = true;
140   _needs_gc              = true;
141   _uses_context          = false;
142   _accepts_filter        = true;
143   _accepts_modifications = false;
144   _accepts_miscellaneous = false;
145   _category              = Command::Category::internal;
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
execute(std::string & output)149 int CmdCompletionIds::execute (std::string& output)
150 {
151   // Apply filter.
152   handleUntil ();
153   handleRecurrence ();
154   Filter filter;
155   std::vector <Task> filtered;
156   filter.subset (filtered);
157 
158   std::vector <int> ids;
159   for (auto& task : filtered)
160     if (task.getStatus () != Task::deleted &&
161         task.getStatus () != Task::completed)
162       ids.push_back (task.id);
163 
164   std::sort (ids.begin (), ids.end ());
165   output = join ("\n", ids) + '\n';
166 
167   Context::getContext ().headers.clear ();
168   return 0;
169 }
170 
171 ////////////////////////////////////////////////////////////////////////////////
CmdZshCompletionIds()172 CmdZshCompletionIds::CmdZshCompletionIds ()
173 {
174   _keyword               = "_zshids";
175   _usage                 = "task <filter> _zshids";
176   _description           = "Shows the IDs and descriptions of matching tasks";
177   _read_only             = true;
178   _displays_id           = true;
179   _needs_gc              = true;
180   _uses_context          = false;
181   _accepts_filter        = true;
182   _accepts_modifications = false;
183   _accepts_miscellaneous = false;
184   _category              = Command::Category::internal;
185 }
186 
187 ////////////////////////////////////////////////////////////////////////////////
execute(std::string & output)188 int CmdZshCompletionIds::execute (std::string& output)
189 {
190   // Apply filter.
191   handleUntil ();
192   handleRecurrence ();
193   Filter filter;
194   std::vector <Task> filtered;
195   filter.subset (filtered);
196 
197   std::stringstream out;
198   for (auto& task : filtered)
199     if (task.getStatus () != Task::deleted &&
200         task.getStatus () != Task::completed)
201       out << task.id
202           << ':'
203           << str_replace(task.get ("description"), ":", zshColonReplacement)
204           << '\n';
205 
206   output = out.str ();
207 
208   Context::getContext ().headers.clear ();
209   return 0;
210 }
211 
212 ////////////////////////////////////////////////////////////////////////////////
CmdUUIDs()213 CmdUUIDs::CmdUUIDs ()
214 {
215   _keyword               = "uuids";
216   _usage                 = "task <filter> uuids";
217   _description           = "Shows the UUIDs of matching tasks, as a space-separated list";
218   _read_only             = true;
219   _displays_id           = false;
220   _needs_gc              = true;
221   _uses_context          = false;
222   _accepts_filter        = true;
223   _accepts_modifications = false;
224   _accepts_miscellaneous = false;
225   _category              = Command::Category::metadata;
226 }
227 
228 ////////////////////////////////////////////////////////////////////////////////
execute(std::string & output)229 int CmdUUIDs::execute (std::string& output)
230 {
231   // Apply filter.
232   handleUntil ();
233   handleRecurrence ();
234   Filter filter;
235   std::vector <Task> filtered;
236   filter.subset (filtered);
237 
238   std::vector <std::string> uuids;
239   uuids.reserve(filtered.size());
240   for (auto& task : filtered)
241     uuids.push_back (task.get ("uuid"));
242 
243   std::sort (uuids.begin (), uuids.end ());
244   output = join (" ", uuids) + '\n';
245 
246   Context::getContext ().headers.clear ();
247   return 0;
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
CmdCompletionUuids()251 CmdCompletionUuids::CmdCompletionUuids ()
252 {
253   _keyword               = "_uuids";
254   _usage                 = "task <filter> _uuids";
255   _description           = "Shows the UUIDs of matching tasks, as a list";
256   _read_only             = true;
257   _displays_id           = false;
258   _needs_gc              = true;
259   _uses_context          = false;
260   _accepts_filter        = true;
261   _accepts_modifications = false;
262   _accepts_miscellaneous = false;
263   _category              = Command::Category::internal;
264 }
265 
266 ////////////////////////////////////////////////////////////////////////////////
execute(std::string & output)267 int CmdCompletionUuids::execute (std::string& output)
268 {
269   // Apply filter.
270   handleUntil ();
271   handleRecurrence ();
272   Filter filter;
273   std::vector <Task> filtered;
274   filter.subset (filtered);
275 
276   std::vector <std::string> uuids;
277   uuids.reserve(filtered.size());
278   for (auto& task : filtered)
279     uuids.push_back (task.get ("uuid"));
280 
281   std::sort (uuids.begin (), uuids.end ());
282   output = join ("\n", uuids) + '\n';
283 
284   Context::getContext ().headers.clear ();
285   return 0;
286 }
287 
288 ////////////////////////////////////////////////////////////////////////////////
CmdZshCompletionUuids()289 CmdZshCompletionUuids::CmdZshCompletionUuids ()
290 {
291   _keyword               = "_zshuuids";
292   _usage                 = "task <filter> _zshuuids";
293   _description           = "Shows the UUIDs and descriptions of matching tasks";
294   _read_only             = true;
295   _displays_id           = false;
296   _needs_gc              = true;
297   _uses_context          = false;
298   _accepts_filter        = true;
299   _accepts_modifications = false;
300   _accepts_miscellaneous = false;
301   _category              = Command::Category::internal;
302 }
303 
304 ////////////////////////////////////////////////////////////////////////////////
execute(std::string & output)305 int CmdZshCompletionUuids::execute (std::string& output)
306 {
307   // Apply filter.
308   handleUntil ();
309   handleRecurrence ();
310   Filter filter;
311   std::vector <Task> filtered;
312   filter.subset (filtered);
313 
314   std::stringstream out;
315   for (auto& task : filtered)
316     out << task.get ("uuid")
317         << ':'
318         << str_replace (task.get ("description"), ":", zshColonReplacement)
319         << '\n';
320 
321   output = out.str ();
322 
323   Context::getContext ().headers.clear ();
324   return 0;
325 }
326 
327 ///////////////////////////////////////////////////////////////////////////////
328