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

GothenburgBitFactory / taskwarrior / 14565012032

21 Apr 2025 12:51AM UTC coverage: 85.263% (+0.01%) from 85.25%
14565012032

push

github

web-flow
Add uuid UDA type (#3827)

Mainly so that UDAs that refer to another task can be formated as
"short".

21 of 22 new or added lines in 3 files covered. (95.45%)

1 existing line in 1 file now uncovered.

19579 of 22963 relevant lines covered (85.26%)

23438.16 hits per line

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

97.5
/src/columns/Column.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 <ColDepends.h>
31
#include <ColDescription.h>
32
#include <ColDue.h>
33
#include <ColEnd.h>
34
#include <ColEntry.h>
35
#include <ColID.h>
36
#include <ColIMask.h>
37
#include <ColLast.h>
38
#include <ColMask.h>
39
#include <ColModified.h>
40
#include <ColParent.h>
41
#include <ColProject.h>
42
#include <ColRType.h>
43
#include <ColRecur.h>
44
#include <ColScheduled.h>
45
#include <ColStart.h>
46
#include <ColStatus.h>
47
#include <ColTags.h>
48
#include <ColTemplate.h>
49
#include <ColUDA.h>
50
#include <ColUUID.h>
51
#include <ColUntil.h>
52
#include <ColUrgency.h>
53
#include <ColWait.h>
54
#include <Column.h>
55
#include <Context.h>
56
#include <format.h>
57
#include <shared.h>
58

59
#include <algorithm>
60
#include <set>
61

62
////////////////////////////////////////////////////////////////////////////////
63
// Supports the complete column definition:
64
//
65
//   <type>[.<format>]
66
//
67
Column* Column::factory(const std::string& name, const std::string& report) {
7,626✔
68
  // Decompose name into type and style.
69
  auto dot = name.find('.');
7,626✔
70
  std::string column_name;
7,626✔
71
  std::string column_style;
7,626✔
72
  if (dot != std::string::npos) {
7,626✔
73
    column_name = name.substr(0, dot);
4,247✔
74
    column_style = name.substr(dot + 1);
4,247✔
75
  } else {
76
    column_name = name;
3,379✔
77
    column_style = "default";
3,379✔
78
  }
79

80
  Column* c;
81
  if (column_name == "depends")
7,626✔
82
    c = new ColumnDepends();
562✔
83
  else if (column_name == "description")
7,064✔
84
    c = new ColumnDescription();
627✔
85
  else if (column_name == "due")
6,437✔
86
    c = new ColumnDue();
586✔
87
  else if (column_name == "end")
5,851✔
88
    c = new ColumnEnd();
96✔
89
  else if (column_name == "entry")
5,755✔
90
    c = new ColumnEntry();
477✔
91
  else if (column_name == "id")
5,278✔
92
    c = new ColumnID();
658✔
93
  else if (column_name == "imask")
4,620✔
94
    c = new ColumnIMask();
3✔
95
  else if (column_name == "last")
4,617✔
96
    c = new ColumnLast();
×
97
  else if (column_name == "mask")
4,617✔
98
    c = new ColumnMask();
3✔
99
  else if (column_name == "modified")
4,614✔
100
    c = new ColumnModified();
20✔
101
  else if (column_name == "parent")
4,594✔
102
    c = new ColumnParent();
3✔
103
  else if (column_name == "project")
4,591✔
104
    c = new ColumnProject();
575✔
105
  else if (column_name == "recur")
4,016✔
106
    c = new ColumnRecur();
568✔
107
  else if (column_name == "rtype")
3,448✔
108
    c = new ColumnRType();
×
109
  else if (column_name == "scheduled")
3,448✔
110
    c = new ColumnScheduled();
549✔
111
  else if (column_name == "start")
2,899✔
112
    c = new ColumnStart();
556✔
113
  else if (column_name == "status")
2,343✔
114
    c = new ColumnStatus();
84✔
115
  else if (column_name == "tags")
2,259✔
116
    c = new ColumnTags();
576✔
117
  else if (column_name == "template")
1,683✔
118
    c = new ColumnTemplate();
×
119
  else if (column_name == "until")
1,683✔
120
    c = new ColumnUntil();
549✔
121
  else if (column_name == "urgency")
1,134✔
122
    c = new ColumnUrgency();
345✔
123
  else if (column_name == "uuid")
789✔
124
    c = new ColumnUUID();
96✔
125
  else if (column_name == "wait")
693✔
126
    c = new ColumnWait();
205✔
127

128
  // UDA.
129
  else if (Context::getContext().config.has("uda." + column_name + ".type"))
488✔
130
    c = Column::uda(column_name);
486✔
131

132
  else
133
    throw format("Unrecognized column name '{1}'.", column_name);
6✔
134

135
  c->setReport(report);
7,624✔
136
  c->setStyle(column_style);
7,624✔
137
  return c;
7,612✔
138
}
7,640✔
139

140
////////////////////////////////////////////////////////////////////////////////
141
// Bulk column instantiation.
142
void Column::factory(std::map<std::string, Column*>& all) {
4,578✔
143
  Column* c;
144

145
  c = new ColumnDepends();
4,578✔
146
  all[c->_name] = c;
4,578✔
147
  c = new ColumnDescription();
4,578✔
148
  all[c->_name] = c;
4,578✔
149
  c = new ColumnDue();
4,578✔
150
  all[c->_name] = c;
4,578✔
151
  c = new ColumnEnd();
4,578✔
152
  all[c->_name] = c;
4,578✔
153
  c = new ColumnEntry();
4,578✔
154
  all[c->_name] = c;
4,578✔
155
  c = new ColumnID();
4,578✔
156
  all[c->_name] = c;
4,578✔
157
  c = new ColumnIMask();
4,578✔
158
  all[c->_name] = c;
4,578✔
159
  c = new ColumnLast();
4,578✔
160
  all[c->_name] = c;
4,578✔
161
  c = new ColumnMask();
4,578✔
162
  all[c->_name] = c;
4,578✔
163
  c = new ColumnModified();
4,578✔
164
  all[c->_name] = c;
4,578✔
165
  c = new ColumnParent();
4,578✔
166
  all[c->_name] = c;
4,578✔
167
  c = new ColumnProject();
4,578✔
168
  all[c->_name] = c;
4,578✔
169
  c = new ColumnRecur();
4,578✔
170
  all[c->_name] = c;
4,578✔
171
  c = new ColumnRType();
4,578✔
172
  all[c->_name] = c;
4,578✔
173
  c = new ColumnScheduled();
4,578✔
174
  all[c->_name] = c;
4,578✔
175
  c = new ColumnStart();
4,578✔
176
  all[c->_name] = c;
4,578✔
177
  c = new ColumnStatus();
4,578✔
178
  all[c->_name] = c;
4,578✔
179
  c = new ColumnTags();
4,578✔
180
  all[c->_name] = c;
4,578✔
181
  c = new ColumnTemplate();
4,578✔
182
  all[c->_name] = c;
4,578✔
183
  c = new ColumnUntil();
4,578✔
184
  all[c->_name] = c;
4,578✔
185
  c = new ColumnUrgency();
4,578✔
186
  all[c->_name] = c;
4,578✔
187
  c = new ColumnUUID();
4,578✔
188
  all[c->_name] = c;
4,578✔
189
  c = new ColumnWait();
4,578✔
190
  all[c->_name] = c;
4,578✔
191

192
  Column::uda(all);
4,578✔
193
}
4,578✔
194

195
////////////////////////////////////////////////////////////////////////////////
196
void Column::uda(std::map<std::string, Column*>& all) {
4,578✔
197
  // For each UDA, instantiate and initialize ColumnUDA.
198
  std::set<std::string> udas;
4,578✔
199

200
  for (const auto& i : Context::getContext().config) {
1,125,520✔
201
    if (i.first.substr(0, 4) == "uda.") {
1,120,942✔
202
      std::string::size_type period = 4;  // One byte after the first '.'.
14,918✔
203

204
      if ((period = i.first.find('.', period)) != std::string::npos)
14,918✔
205
        udas.insert(i.first.substr(4, period - 4));
14,918✔
206
    }
207
  }
208

209
  for (const auto& uda : udas) {
9,948✔
210
    if (all.find(uda) != all.end())
5,370✔
211
      throw format("The UDA named '{1}' is the same as a core attribute, and is not permitted.",
212
                   uda);
×
213

214
    Column* c = Column::uda(uda);
5,370✔
215
    if (c) all[c->_name] = c;
5,370✔
216
  }
217
}
4,578✔
218

219
////////////////////////////////////////////////////////////////////////////////
220
Column* Column::uda(const std::string& name) {
5,856✔
221
  auto type = Context::getContext().config.get("uda." + name + ".type");
5,856✔
222
  auto label = Context::getContext().config.get("uda." + name + ".label");
5,856✔
223
  auto values = Context::getContext().config.get("uda." + name + ".values");
5,856✔
224

225
  if (type == "string") {
5,856✔
226
    auto c = new ColumnUDAString();
5,305✔
227
    c->_name = name;
5,305✔
228
    c->_label = label;
5,305✔
229
    if (values != "") c->_values = split(values, ',');
5,305✔
230
    return c;
5,305✔
231
  } else if (type == "numeric") {
551✔
232
    auto c = new ColumnUDANumeric();
214✔
233
    c->_name = name;
214✔
234
    c->_label = label;
214✔
235
    if (values != "") c->_values = split(values, ',');
214✔
236
    return c;
214✔
237
  } else if (type == "date") {
337✔
238
    auto c = new ColumnUDADate();
125✔
239
    c->_name = name;
125✔
240
    c->_label = label;
125✔
241
    if (values != "") c->_values = split(values, ',');
125✔
242
    return c;
125✔
243
  } else if (type == "duration") {
212✔
244
    auto c = new ColumnUDADuration();
119✔
245
    c->_name = name;
119✔
246
    c->_label = label;
119✔
247
    if (values != "") c->_values = split(values, ',');
119✔
248
    return c;
119✔
249
  } else if (type == "uuid") {
93✔
250
    auto c = new ColumnUDAUUID();
16✔
251
    c->_name = name;
16✔
252
    c->_label = label;
16✔
253
    return c;
16✔
254
  } else if (type != "")
77✔
255
    throw std::string(
256
        "User defined attributes may only be of type 'string', 'uuid', date', 'duration' or "
NEW
257
        "'numeric'.");
×
258

259
  return nullptr;
77✔
260
}
5,856✔
261

262
////////////////////////////////////////////////////////////////////////////////
263
Column::Column()
118,212✔
264
    : _name(""),
118,212✔
265
      _type("string"),
236,424✔
266
      _style("default"),
236,424✔
267
      _label(""),
236,424✔
268
      _report(""),
236,424✔
269
      _modifiable(true),
118,212✔
270
      _uda(false),
118,212✔
271
      _fixed_width(false) {}
236,424✔
272

273
////////////////////////////////////////////////////////////////////////////////
274
void Column::renderHeader(std::vector<std::string>& lines, int width, Color& color) {
2,780✔
275
  if (Context::getContext().verbose("label") && _label != "") {
8,340✔
276
    // Create a basic label.
277
    std::string header;
2,008✔
278
    header.reserve(width);
2,008✔
279
    header = _label;
2,008✔
280

281
    // Create a fungible copy.
282
    Color c = color;
2,008✔
283

284
    // Now underline the header, or add a dashed line.
285
    if (Context::getContext().color() && Context::getContext().config.getBoolean("fontunderline")) {
2,202✔
286
      c.blend(Color(Color::nocolor, Color::nocolor, true, false, false));
89✔
287
      lines.push_back(c.colorize(leftJustify(header, width)));
89✔
288
    } else {
289
      lines.push_back(c.colorize(leftJustify(header, width)));
1,919✔
290
      lines.push_back(c.colorize(std::string(width, '-')));
3,838✔
291
    }
292
  }
2,008✔
293
}
2,780✔
294

295
////////////////////////////////////////////////////////////////////////////////
296
void Column::setStyle(const std::string& style) {
7,624✔
297
  if (style != "default" && std::find(_styles.begin(), _styles.end(), style) == _styles.end())
7,624✔
298
    throw format("Unrecognized column format '{1}.{2}'", _name, style);
36✔
299

300
  _style = style;
7,612✔
301
}
7,612✔
302

303
////////////////////////////////////////////////////////////////////////////////
304
// All integer values are right-justified.
305
void Column::renderInteger(std::vector<std::string>& lines, int width, Color& color, int value) {
1,202✔
306
  lines.push_back(color.colorize(rightJustify(value, width)));
1,202✔
307
}
1,202✔
308

309
////////////////////////////////////////////////////////////////////////////////
310
// All floating point values are right-justified.
311
void Column::renderDouble(std::vector<std::string>& lines, int width, Color& color, double value) {
476✔
312
  lines.push_back(color.colorize(rightJustify(format(value, 4, 3), width)));
476✔
313
}
476✔
314

315
////////////////////////////////////////////////////////////////////////////////
316
void Column::renderStringLeft(std::vector<std::string>& lines, int width, Color& color,
2,639✔
317
                              const std::string& value) {
318
  lines.push_back(color.colorize(leftJustify(value, width)));
2,639✔
319
}
2,639✔
320

321
////////////////////////////////////////////////////////////////////////////////
322
void Column::renderStringRight(std::vector<std::string>& lines, int width, Color& color,
1,298✔
323
                               const std::string& value) {
324
  lines.push_back(color.colorize(rightJustify(value, width)));
1,298✔
325
}
1,298✔
326

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

© 2026 Coveralls, Inc