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

GothenburgBitFactory / taskwarrior / 11420355627

19 Oct 2024 08:00PM UTC coverage: 84.853% (+0.6%) from 84.223%
11420355627

push

github

web-flow
Pass rc.weekstart to libshared for ISO8601 weeknum parsing if "monday" (#3654)

* libshared: bump for weekstart, epoch defines, eopww fix

mainly those visible changes, and miscellaneous others

see GothenburgBitFactory/taskwarrior#3623 (weekstart)
see GothenburgBitFactory/taskwarrior#3651 (epoch limit defines)
see GothenburgBitFactory/libshared#73 (eopww fix)

* Initialize libshared's weekstart from user's rc.weekstart config

This enables use of newer libshared code that can parse week numbers
according to ISO8601 instead of existing code which is always using
Sunday-based weeks.  To get ISO behavior, set rc.weekstart=monday.
Default is still Sunday / old algorithm, as before, since Sunday is in
the hardcoded default rcfile.

Weekstart does not yet fix week-relative shortcuts, which will still
always use Monday.

See #3623 for further details.

4 of 6 new or added lines in 2 files covered. (66.67%)

993 existing lines in 25 files now uncovered.

19019 of 22414 relevant lines covered (84.85%)

23067.98 hits per line

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

81.74
/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
// cmake.h include header must come first
29

30
#include <Context.h>
31
#include <Duration.h>
32
#include <Task.h>
33
#include <format.h>
34
#include <shared.h>
35
#include <stdlib.h>
36
#include <util.h>
37

38
#include <algorithm>
39
#include <list>
40
#include <map>
41
#include <string>
42
#include <vector>
43

44
static std::vector<Task>* global_data = nullptr;
45
static std::vector<std::string> global_keys;
46
static bool sort_compare(int, int);
47

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

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

56
  // Only sort if necessary.
57
  if (order.size()) std::stable_sort(order.begin(), order.end(), sort_compare);
709✔
58

59
  Context::getContext().time_sort_us += timer.total_us();
709✔
60
}
709✔
61

62
void sort_projects(std::list<std::pair<std::string, int>>& sorted,
16✔
63
                   std::map<std::string, int>& allProjects) {
64
  for (auto& project : allProjects) {
46✔
65
    const std::vector<std::string> parents = extractParents(project.first);
30✔
66
    if (parents.size()) {
30✔
67
      // if parents exist: store iterator position of last parent
68
      std::list<std::pair<std::string, int>>::iterator parent_pos;
6✔
69
      for (auto& parent : parents) {
12✔
70
        parent_pos = std::find_if(
6✔
71
            sorted.begin(), sorted.end(),
72
            [&parent](const std::pair<std::string, int>& item) { return item.first == parent; });
17✔
73

74
        // if parent does not exist yet: insert into sorted view
75
        if (parent_pos == sorted.end()) sorted.emplace_back(parent, 1);
6✔
76
      }
77

78
      // insert new element below latest parent
79
      sorted.insert((parent_pos == sorted.end()) ? parent_pos : ++parent_pos, project);
6✔
80
    } else {
81
      // if has no parents: simply push to end of list
82
      sorted.push_back(project);
24✔
83
    }
84
  }
30✔
85
}
16✔
86

87
void sort_projects(std::list<std::pair<std::string, int>>& sorted,
6✔
88
                   std::map<std::string, bool>& allProjects) {
89
  std::map<std::string, int> allProjectsInt;
6✔
90
  for (auto& p : allProjects) allProjectsInt[p.first] = (int)p.second;
17✔
91

92
  sort_projects(sorted, allProjectsInt);
6✔
93
}
6✔
94

95
////////////////////////////////////////////////////////////////////////////////
96
// Re-implementation, using direct Task access instead of data copies that
97
// require re-parsing.
98
//
99
// Essentially a static implementation of a dynamic operator<.
100
static bool sort_compare(int left, int right) {
1,989✔
101
  std::string field;
1,989✔
102
  bool ascending;
103
  bool breakIndicator;
104
  Column* column;
105
  int left_number;
106
  int right_number;
107
  float left_real;
108
  float right_real;
109

110
  for (auto& k : global_keys) {
3,873✔
111
    Context::getContext().decomposeSortField(k, field, ascending, breakIndicator);
3,513✔
112

113
    // Urgency.
114
    if (field == "urgency") {
3,513✔
115
      left_real = (*global_data)[left].urgency();
143✔
116
      right_real = (*global_data)[right].urgency();
143✔
117

118
      if (left_real == right_real) continue;
143✔
119

120
      return ascending ? (left_real < right_real) : (left_real > right_real);
1,629✔
121
    }
122

123
    // Number.
124
    else if (field == "id") {
3,370✔
125
      left_number = (*global_data)[left].id;
299✔
126
      right_number = (*global_data)[right].id;
299✔
127

128
      if (left_number == right_number) continue;
299✔
129

130
      return ascending ? (left_number < right_number) : (left_number > right_number);
87✔
131
    }
132

133
    // String.
134
    else if (field == "description" || field == "project" || field == "status" || field == "tags" ||
7,653✔
135
             field == "uuid" || field == "parent" || field == "imask" || field == "mask") {
7,653✔
136
      auto left_string = (*global_data)[left].get_ref(field);
1,116✔
137
      auto right_string = (*global_data)[right].get_ref(field);
1,116✔
138

139
      if (left_string == right_string) continue;
1,116✔
140

141
      return ascending ? (left_string < right_string) : (left_string > right_string);
967✔
142
    }
2,232✔
143

144
    // Due Date.
145
    else if (field == "due" || field == "end" || field == "entry" || field == "start" ||
3,531✔
146
             field == "until" || field == "wait" || field == "modified" || field == "scheduled") {
3,531✔
147
      auto left_string = (*global_data)[left].get_ref(field);
1,903✔
148
      auto right_string = (*global_data)[right].get_ref(field);
1,903✔
149

150
      if (left_string != "" && right_string == "") return true;
1,903✔
151

152
      if (left_string == "" && right_string != "") return false;
1,882✔
153

154
      if (left_string == right_string) continue;
1,880✔
155

156
      return ascending ? (left_string < right_string) : (left_string > right_string);
444✔
157
    }
3,806✔
158

159
    // Depends string.
160
    else if (field == "depends") {
52✔
161
      // Raw data is an un-sorted list of UUIDs.  We just need a stable
162
      // sort, so we sort them lexically.
163
      auto left_deps = (*global_data)[left].getDependencyUUIDs();
×
164
      std::sort(left_deps.begin(), left_deps.end());
×
165
      auto right_deps = (*global_data)[right].getDependencyUUIDs();
×
166
      std::sort(right_deps.begin(), right_deps.end());
×
167

168
      if (left_deps == right_deps) continue;
×
169

170
      if (left_deps.size() == 0 && right_deps.size() > 0) return ascending;
×
171

172
      if (left_deps.size() > 0 && right_deps.size() == 0) return !ascending;
×
173

174
      // Sort on the first dependency.
175
      left_number = Context::getContext().tdb2.id(left_deps[0]);
×
176
      right_number = Context::getContext().tdb2.id(right_deps[0]);
×
177

178
      if (left_number == right_number) continue;
×
179

180
      return ascending ? (left_number < right_number) : (left_number > right_number);
×
181
    }
182

183
    // Duration.
184
    else if (field == "recur") {
52✔
185
      auto left_string = (*global_data)[left].get_ref(field);
7✔
186
      auto right_string = (*global_data)[right].get_ref(field);
7✔
187

188
      if (left_string == right_string) continue;
7✔
189

190
      Duration left_duration(left_string);
7✔
191
      Duration right_duration(right_string);
7✔
192
      return ascending ? (left_duration < right_duration) : (left_duration > right_duration);
7✔
193
    }
14✔
194

195
    // UDAs.
196
    else if ((column = Context::getContext().columns[field]) != nullptr) {
45✔
197
      std::string type = column->type();
45✔
198
      if (type == "numeric") {
45✔
199
        auto left_real = strtof(((*global_data)[left].get_ref(field)).c_str(), nullptr);
11✔
200
        auto right_real = strtof(((*global_data)[right].get_ref(field)).c_str(), nullptr);
11✔
201

202
        if (left_real == right_real) continue;
11✔
203

204
        return ascending ? (left_real < right_real) : (left_real > right_real);
11✔
205
      } else if (type == "string") {
34✔
206
        auto left_string = (*global_data)[left].get_ref(field);
33✔
207
        auto right_string = (*global_data)[right].get_ref(field);
33✔
208

209
        if (left_string == right_string) continue;
33✔
210

211
        // UDAs of the type string can have custom sort orders, which need to be considered.
212
        auto order = Task::customOrder.find(field);
32✔
213
        if (order != Task::customOrder.end()) {
32✔
214
          // Guaranteed to be found, because of ColUDA::validate ().
215
          auto posLeft = std::find(order->second.begin(), order->second.end(), left_string);
23✔
216
          auto posRight = std::find(order->second.begin(), order->second.end(), right_string);
23✔
217
          return ascending ? (posLeft < posRight) : (posLeft > posRight);
23✔
218
        } else {
219
          // Empty values are unconditionally last, if no custom order was specified.
220
          if (left_string == "")
9✔
221
            return false;
5✔
222
          else if (right_string == "")
4✔
223
            return true;
2✔
224

225
          return ascending ? (left_string < right_string) : (left_string > right_string);
2✔
226
        }
227
      }
66✔
228

229
      else if (type == "date") {
1✔
230
        auto left_string = (*global_data)[left].get_ref(field);
1✔
231
        auto right_string = (*global_data)[right].get_ref(field);
1✔
232

233
        if (left_string != "" && right_string == "") return true;
1✔
234

UNCOV
235
        if (left_string == "" && right_string != "") return false;
×
236

237
        if (left_string == right_string) continue;
×
238

239
        return ascending ? (left_string < right_string) : (left_string > right_string);
×
240
      } else if (type == "duration") {
2✔
241
        auto left_string = (*global_data)[left].get_ref(field);
×
242
        auto right_string = (*global_data)[right].get_ref(field);
×
243

244
        if (left_string == right_string) continue;
×
245

246
        Duration left_duration(left_string);
×
247
        Duration right_duration(right_string);
×
248
        return ascending ? (left_duration < right_duration) : (left_duration > right_duration);
×
249
      }
250
    } else
45✔
251
      throw format("The '{1}' column is not a valid sort field.", field);
×
252
  }
253

254
  return false;
360✔
255
}
1,989✔
256

257
////////////////////////////////////////////////////////////////////////////////
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