• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 10019625030

20 Jul 2024 10:46AM UTC coverage: 84.395% (-0.009%) from 84.404%
10019625030

push

github

web-flow
Fix conversion from `TCStatus::Unknown` (#3561)

Before this patch, the messsage would be "unknown TCStatus 4294967295"
(i.e. `u32::MAX`) instead of "unknown TCStatus -1".

19362 of 22942 relevant lines covered (84.4%)

20317.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.48
/src/sort.cpp
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 <algorithm>
29
#include <vector>
30
#include <list>
31
#include <map>
32
#include <string>
33
#include <stdlib.h>
34
#include <Context.h>
35
#include <Duration.h>
36
#include <Task.h>
37
#include <shared.h>
38
#include <util.h>
39
#include <format.h>
40

41
static std::vector <Task>* global_data = nullptr;
42
static std::vector <std::string> global_keys;
43
static bool sort_compare (int, int);
44

45
////////////////////////////////////////////////////////////////////////////////
46
void sort_tasks (
682✔
47
  std::vector <Task>& data,
48
  std::vector <int>& order,
49
  const std::string& keys)
50
{
51
  Timer timer;
682✔
52
  global_data = &data;
682✔
53

54
  // Split the key defs.
55
  global_keys = split (keys, ',');
682✔
56

57
  // Only sort if necessary.
58
  if (order.size ())
682✔
59
    std::stable_sort (order.begin (), order.end (), sort_compare);
610✔
60

61
  Context::getContext ().time_sort_us += timer.total_us ();
682✔
62
}
682✔
63

64
void sort_projects (
16✔
65
    std::list <std::pair <std::string, int>>& sorted,
66
    std::map <std::string, int>& allProjects)
67
{
68
  for (auto& project : allProjects)
46✔
69
  {
70
    const std::vector <std::string> parents = extractParents (project.first);
30✔
71
    if (parents.size ())
30✔
72
    {
73
      // if parents exist: store iterator position of last parent
74
      std::list <std::pair <std::string, int>>::iterator parent_pos;
6✔
75
      for (auto& parent : parents)
12✔
76
      {
77
        parent_pos = std::find_if (sorted.begin (), sorted.end (),
6✔
78
            [&parent](const std::pair <std::string, int>& item) { return item.first == parent; });
17✔
79
        
80
        // if parent does not exist yet: insert into sorted view
81
        if (parent_pos == sorted.end ())
6✔
82
          sorted.emplace_back (parent, 1);
2✔
83
      }
84
      
85
      // insert new element below latest parent
86
      sorted.insert ((parent_pos == sorted.end ()) ? parent_pos : ++parent_pos, project);
6✔
87
    }
88
    else
89
    {
90
      // if has no parents: simply push to end of list
91
      sorted.push_back (project);
24✔
92
    }
93
  }
30✔
94
}
16✔
95

96
void sort_projects (
6✔
97
    std::list <std::pair <std::string, int>>& sorted,
98
    std::map <std::string, bool>& allProjects)
99
{
100
  std::map <std::string, int> allProjectsInt;
6✔
101
  for (auto& p : allProjects)
17✔
102
    allProjectsInt[p.first] = (int) p.second;
11✔
103
  
104
  sort_projects (sorted, allProjectsInt);
6✔
105
}
6✔
106

107

108
////////////////////////////////////////////////////////////////////////////////
109
// Re-implementation, using direct Task access instead of data copies that
110
// require re-parsing.
111
//
112
// Essentially a static implementation of a dynamic operator<.
113
static bool sort_compare (int left, int right)
1,941✔
114
{
115
  std::string field;
1,941✔
116
  bool ascending;
117
  bool breakIndicator;
118
  Column* column;
119
  int left_number;
120
  int right_number;
121
  float left_real;
122
  float right_real;
123

124
  for (auto& k : global_keys)
3,890✔
125
  {
126
    Context::getContext ().decomposeSortField (k, field, ascending, breakIndicator);
3,464✔
127

128
    // Urgency.
129
    if (field == "urgency")
3,464✔
130
    {
131
      left_real  = (*global_data)[left].urgency ();
141✔
132
      right_real = (*global_data)[right].urgency ();
141✔
133

134
      if (left_real == right_real)
141✔
135
        continue;
84✔
136

137
      return ascending ? (left_real < right_real)
57✔
138
                       : (left_real > right_real);
1,515✔
139
    }
140

141
    // Number.
142
    else if (field == "id")
3,323✔
143
    {
144
      left_number  = (*global_data)[left].id;
310✔
145
      right_number = (*global_data)[right].id;
310✔
146

147
      if (left_number == right_number)
310✔
148
        continue;
217✔
149

150
      return ascending ? (left_number < right_number)
93✔
151
                       : (left_number > right_number);
93✔
152
    }
153

154
    // String.
155
    else if (field == "description" ||
5,370✔
156
             field == "project"     ||
4,468✔
157
             field == "status"      ||
4,222✔
158
             field == "tags"        ||
4,222✔
159
             field == "uuid"        ||
4,005✔
160
             field == "parent"      ||
3,788✔
161
             field == "imask"       ||
7,264✔
162
             field == "mask")
1,894✔
163
    {
164
      auto left_string  = (*global_data)[left].get_ref  (field);
1,119✔
165
      auto right_string = (*global_data)[right].get_ref (field);
1,119✔
166

167
      if (left_string == right_string)
1,119✔
168
        continue;
147✔
169

170
      return ascending ? (left_string < right_string)
972✔
171
                       : (left_string > right_string);
972✔
172
    }
2,238✔
173

174
    // Due Date.
175
    else if (field == "due"      ||
3,350✔
176
             field == "end"      ||
2,880✔
177
             field == "entry"    ||
2,427✔
178
             field == "start"    ||
1,066✔
179
             field == "until"    ||
126✔
180
             field == "wait"     ||
126✔
181
             field == "modified" ||
3,413✔
182
             field == "scheduled")
53✔
183
    {
184
      auto left_string  = (*global_data)[left].get_ref  (field);
1,841✔
185
      auto right_string = (*global_data)[right].get_ref (field);
1,841✔
186

187
      if (left_string != "" && right_string == "")
1,841✔
188
        return true;
21✔
189

190
      if (left_string == "" && right_string != "")
1,820✔
191
        return false;
2✔
192

193
      if (left_string == right_string)
1,818✔
194
        continue;
1,500✔
195

196
      return ascending ? (left_string < right_string)
318✔
197
                       : (left_string > right_string);
318✔
198
    }
3,682✔
199

200
    // Depends string.
201
    else if (field == "depends")
53✔
202
    {
203
      // Raw data is an un-sorted list of UUIDs.  We just need a stable
204
      // sort, so we sort them lexically.
205
      auto left_deps  = (*global_data)[left].getDependencyUUIDs ();
×
206
      std::sort(left_deps.begin(), left_deps.end());
×
207
      auto right_deps = (*global_data)[right].getDependencyUUIDs ();
×
208
      std::sort(right_deps.begin(), right_deps.end());
×
209

210
      if (left_deps == right_deps)
×
211
        continue;
×
212

213
      if (left_deps.size () == 0 && right_deps.size () > 0)
×
214
        return ascending;
×
215

216
      if (left_deps.size () > 0 && right_deps.size () == 0)
×
217
        return !ascending;
×
218

219
      // Sort on the first dependency.
220
      left_number  = Context::getContext ().tdb2.id (left_deps[0]);
×
221
      right_number = Context::getContext ().tdb2.id (right_deps[0]);
×
222

223
      if (left_number == right_number)
×
224
        continue;
×
225

226
      return ascending ? (left_number < right_number)
×
227
                       : (left_number > right_number);
×
228
    }
229

230
    // Duration.
231
    else if (field == "recur")
53✔
232
    {
233
      auto left_string  = (*global_data)[left].get_ref  (field);
7✔
234
      auto right_string = (*global_data)[right].get_ref (field);
7✔
235

236
      if (left_string == right_string)
7✔
237
        continue;
×
238

239
      Duration left_duration (left_string);
7✔
240
      Duration right_duration (right_string);
7✔
241
      return ascending ? (left_duration < right_duration)
7✔
242
                       : (left_duration > right_duration);
7✔
243
    }
14✔
244

245
    // UDAs.
246
    else if ((column = Context::getContext ().columns[field]) != nullptr)
46✔
247
    {
248
      std::string type = column->type ();
46✔
249
      if (type == "numeric")
46✔
250
      {
251
        auto left_real  = strtof (((*global_data)[left].get_ref  (field)).c_str (), nullptr);
12✔
252
        auto right_real = strtof (((*global_data)[right].get_ref (field)).c_str (), nullptr);
12✔
253

254
        if (left_real == right_real)
12✔
255
          continue;
×
256

257
        return ascending ? (left_real < right_real)
12✔
258
                         : (left_real > right_real);
12✔
259
      }
260
      else if (type == "string")
34✔
261
      {
262
        auto left_string = (*global_data)[left].get_ref (field);
33✔
263
        auto right_string = (*global_data)[right].get_ref (field);
33✔
264

265
        if (left_string == right_string)
33✔
266
          continue;
1✔
267

268
        // UDAs of the type string can have custom sort orders, which need to be considered.
269
        auto order = Task::customOrder.find (field);
32✔
270
        if (order != Task::customOrder.end ())
32✔
271
        {
272
          // Guaranteed to be found, because of ColUDA::validate ().
273
          auto posLeft  = std::find (order->second.begin (), order->second.end (), left_string);
23✔
274
          auto posRight = std::find (order->second.begin (), order->second.end (), right_string);
23✔
275
          return ascending ? (posLeft < posRight) : (posLeft > posRight);
23✔
276
        }
277
        else
278
        {
279
          // Empty values are unconditionally last, if no custom order was specified.
280
          if (left_string == "")
9✔
281
            return false;
5✔
282
          else if (right_string == "")
4✔
283
            return true;
2✔
284

285
          return ascending ? (left_string < right_string)
2✔
286
                           : (left_string > right_string);
2✔
287
        }
288
      }
66✔
289

290
      else if (type == "date")
1✔
291
      {
292
        auto left_string  = (*global_data)[left].get_ref  (field);
1✔
293
        auto right_string = (*global_data)[right].get_ref (field);
1✔
294

295
        if (left_string != "" && right_string == "")
1✔
296
          return true;
1✔
297

298
        if (left_string == "" && right_string != "")
×
299
          return false;
×
300

301
        if (left_string == right_string)
×
302
          continue;
×
303

304
        return ascending ? (left_string < right_string)
×
305
                         : (left_string > right_string);
×
306
      }
2✔
307
      else if (type == "duration")
×
308
      {
309
        auto left_string  = (*global_data)[left].get_ref  (field);
×
310
        auto right_string = (*global_data)[right].get_ref (field);
×
311

312
        if (left_string == right_string)
×
313
          continue;
×
314

315
        Duration left_duration (left_string);
×
316
        Duration right_duration (right_string);
×
317
        return ascending ? (left_duration < right_duration)
×
318
                         : (left_duration > right_duration);
×
319
      }
320
    }
46✔
321
    else
322
      throw format ("The '{1}' column is not a valid sort field.", field);
×
323
  }
324

325
  return false;
426✔
326
}
1,941✔
327

328
////////////////////////////////////////////////////////////////////////////////
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc