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

GothenburgBitFactory / taskwarrior / 11703734187

06 Nov 2024 12:40PM UTC coverage: 85.073% (+0.08%) from 84.996%
11703734187

push

github

web-flow
bump libshared for bold 256color support (#3670)

In particular, commit https://github.com/GothenburgBitFactory/libshared/commit/47a750c38.

19138 of 22496 relevant lines covered (85.07%)

22922.84 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) {
2,013✔
101
  std::string field;
2,013✔
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,898✔
111
    Context::getContext().decomposeSortField(k, field, ascending, breakIndicator);
3,541✔
112

113
    // Urgency.
114
    if (field == "urgency") {
3,541✔
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,656✔
121
    }
122

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

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

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

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

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

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

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

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

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

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

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

159
    // Depends string.
160
    else if (field == "depends") {
54✔
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") {
54✔
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) {
47✔
197
      std::string type = column->type();
47✔
198
      if (type == "numeric") {
47✔
199
        auto left_real = strtof(((*global_data)[left].get_ref(field)).c_str(), nullptr);
13✔
200
        auto right_real = strtof(((*global_data)[right].get_ref(field)).c_str(), nullptr);
13✔
201

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

204
        return ascending ? (left_real < right_real) : (left_real > right_real);
13✔
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;
4✔
222
          else if (right_string == "")
5✔
223
            return true;
3✔
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

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
47✔
251
      throw format("The '{1}' column is not a valid sort field.", field);
×
252
  }
253

254
  return false;
357✔
255
}
2,013✔
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