Add uuid UDA type (#3827)

Mainly so that UDAs that refer to another task can be formated as
"short".
This commit is contained in:
Ram-Z 2025-04-21 01:51:38 +01:00 committed by GitHub
parent bae37d9448
commit 31829d61fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 107 additions and 12 deletions

View file

@ -1384,7 +1384,7 @@ if you define a UDA named 'estimate', Taskwarrior will not know that this value
is weeks, hours, minutes, money, or some other resource count. is weeks, hours, minutes, money, or some other resource count.
.TP .TP
.B uda.<name>.type=string|numeric|date|duration .B uda.<name>.type=string|numeric|uuid|date|duration
.RS .RS
Defines a UDA called '<name>', of the specified type. Defines a UDA called '<name>', of the specified type.
.RE .RE

View file

@ -1999,7 +1999,7 @@ void Task::modify(modType type, bool text_required /* = false */) {
// Delegate modification to the column object or their base classes. // Delegate modification to the column object or their base classes.
if (name == "depends" || name == "tags" || name == "recur" || column->type() == "date" || if (name == "depends" || name == "tags" || name == "recur" || column->type() == "date" ||
column->type() == "duration" || column->type() == "numeric" || column->type() == "duration" || column->type() == "numeric" ||
column->type() == "string") { column->type() == "string" || column->type() == "uuid") {
column->modify(*this, value); column->modify(*this, value);
mods = true; mods = true;
} }

View file

@ -297,3 +297,23 @@ void ColumnUDADuration::render(std::vector<std::string>& lines, Task& task, int
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ColumnUDAUUID::ColumnUDAUUID() {
_name = "<uda>";
_type = "uuid";
_style = "long";
_label = "";
_modifiable = true;
_uda = true;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnUDAUUID::validate(const std::string& input) const {
Lexer lex(input);
std::string token;
Lexer::Type type;
return lex.isUUID(token, type, true);
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -31,6 +31,7 @@
#include <ColTypeDuration.h> #include <ColTypeDuration.h>
#include <ColTypeNumeric.h> #include <ColTypeNumeric.h>
#include <ColTypeString.h> #include <ColTypeString.h>
#include <ColUUID.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class ColumnUDAString : public ColumnTypeString { class ColumnUDAString : public ColumnTypeString {
@ -83,5 +84,12 @@ class ColumnUDADuration : public ColumnTypeDuration {
std::vector<std::string> _values; std::vector<std::string> _values;
}; };
////////////////////////////////////////////////////////////////////////////////
class ColumnUDAUUID : public ColumnUUID {
public:
ColumnUDAUUID();
bool validate(const std::string&) const;
};
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -246,9 +246,15 @@ Column* Column::uda(const std::string& name) {
c->_label = label; c->_label = label;
if (values != "") c->_values = split(values, ','); if (values != "") c->_values = split(values, ',');
return c; return c;
} else if (type == "uuid") {
auto c = new ColumnUDAUUID();
c->_name = name;
c->_label = label;
return c;
} else if (type != "") } else if (type != "")
throw std::string( throw std::string(
"User defined attributes may only be of type 'string', 'date', 'duration' or 'numeric'."); "User defined attributes may only be of type 'string', 'uuid', date', 'duration' or "
"'numeric'.");
return nullptr; return nullptr;
} }

View file

@ -109,29 +109,26 @@ class TestUUIDFormats(TestCase):
def setUpClass(cls): def setUpClass(cls):
"""Executed once before any test in the class""" """Executed once before any test in the class"""
cls.t = Task() cls.t = Task()
cls.t.config("report.xxx.columns", "id,uuid") cls.t.config("report.xxx.columns", "uuid")
cls.t.config("verbose", "nothing") cls.t.config("verbose", "nothing")
cls.t("add zero") cls.t("add zero")
code, out, err = cls.t("_get 1.uuid") code, out, err = cls.t("_get 1.uuid")
cls.uuid = out.strip() cls.uuid = out.strip()
def setUp(self):
"""Executed before each test in the class"""
def test_uuid_long(self): def test_uuid_long(self):
"""Verify formatting of 'uuid.long' column""" """Verify formatting of 'uuid.long' column"""
code, out, err = self.t("xxx rc.report.xxx.columns:id,uuid.long") code, out, err = self.t("xxx rc.report.xxx.columns:uuid.long")
self.assertIn(self.uuid, out) self.assertEqual(self.uuid, out.strip())
def test_uuid_short(self): def test_uuid_short(self):
"""Verify formatting of 'uuid.short' column""" """Verify formatting of 'uuid.short' column"""
code, out, err = self.t("xxx rc.report.xxx.columns:id,uuid.short") code, out, err = self.t("xxx rc.report.xxx.columns:uuid.short")
self.assertIn(self.uuid[:7], out) self.assertEqual(self.uuid[:8], out.strip())
def test_uuid_format_unrecognized(self): def test_uuid_format_unrecognized(self):
"""Verify uuid.donkey formatting fails""" """Verify uuid.donkey formatting fails"""
code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,uuid.donkey") code, out, err = self.t.runError("xxx rc.report.xxx.columns:uuid.donkey")
self.assertEqual(err, "Unrecognized column format 'uuid.donkey'\n") self.assertEqual(err, "Unrecognized column format 'uuid.donkey'\n")
@ -482,6 +479,70 @@ start active* ✓
""" """
class TestUDAUUIDFormats(TestCase):
@classmethod
def setUpClass(cls):
"""Executed once before any test in the class"""
cls.t = Task()
cls.t.config("verbose", "nothing")
cls.t.config("uda.uda_uuid.label", "uda_uuid")
cls.t.config("uda.uda_uuid.type", "uuid")
cls.t.config("report.xxx.columns", "uda_uuid")
cls.t("add zero")
code, out, err = cls.t("_get 1.uuid")
cls.t("add uda_uuid:{} one".format(out.strip()))
code, out, err = cls.t("_get 2.uda_uuid")
cls.uda_uuid = out.strip()
def test_uda_uuid_invalid_fails(self):
"""Verify adding invalid uuid fails"""
code, out, err = self.t.runError("add uda_uuid:shrek three")
self.assertNotEqual(code, 0)
self.assertIn("uda_uuid", err.strip())
self.assertIn("shrek", err.strip())
def test_uda_uuid_long(self):
"""Verify formatting of 'uda_uuid.long' column"""
code, out, err = self.t("2 xxx rc.report.xxx.columns:uda_uuid.long")
self.assertEqual(self.uda_uuid, out.strip())
def test_uda_uuid_short(self):
"""Verify formatting of 'uda_uuid.short' column"""
code, out, err = self.t("2 xxx rc.report.xxx.columns:uda_uuid.short")
self.assertEqual(self.uda_uuid[:8], out.strip())
def test_uda_uuid_format_unrecognized(self):
"""Verify uda_uuid.donkey formatting fails"""
code, out, err = self.t.runError("xxx rc.report.xxx.columns:id,uda_uuid.donkey")
self.assertEqual(err, "Unrecognized column format 'uda_uuid.donkey'\n")
class TestUDAUUIDReconfiguredFromString(TestCase):
@classmethod
def setUpClass(cls):
"""Executed once before any test in the class"""
cls.t = Task()
cls.t.config("verbose", "nothing")
cls.t.config("uda.uda_uuid.label", "uda_uuid")
cls.t.config("report.xxx.columns", "uda_uuid")
cls.t.config("uda.uda_uuid.type", "string")
cls.expected_str = 3 * "littlepigs"
cls.t("add uda_uuid:{} one".format(cls.expected_str))
cls.t.config("uda.uda_uuid.type", "uuid")
def test_uda_uuid_long(self):
"""Verify formatting of 'uda_uuid.long' column"""
code, out, err = self.t("1 xxx rc.report.xxx.columns:uda_uuid.long")
self.assertEqual(self.expected_str, out.strip())
def test_uda_uuid_short(self):
"""Verify formatting of 'uda_uuid.short' column"""
code, out, err = self.t("1 xxx rc.report.xxx.columns:uda_uuid.short")
self.assertEqual(self.expected_str[:8], out.strip())
class TestFeature1061(TestCase): class TestFeature1061(TestCase):
def setUp(self): def setUp(self):
"""Executed before each test in the class""" """Executed before each test in the class"""