mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
File Format
- File format 2 (used in version 0.9.3 - 1.5.0) is no longer supported. - Task::parseJSON implemented. - PRODUCT_TASKWARRIOR defined, to facilitate code sharing. - Strings renamed for sharing purposes.
This commit is contained in:
parent
00f8f56c00
commit
6a97017c79
11 changed files with 254 additions and 139 deletions
|
@ -24,6 +24,7 @@ Features
|
|||
+ The 'due' urgency component now uses seconds, not days, in the calculation.
|
||||
+ The 'debug.tls' configuration variable takes an integer which corresponds to
|
||||
the GnuTLS log level. For debugging.
|
||||
+ File format 2 (used in version 0.9.3 - 1.5.0) is no longer supported.
|
||||
|
||||
Bugs
|
||||
+ #1196 Now builds on Hurd (thanks to Jakub Wilk).
|
||||
|
|
2
NEWS
2
NEWS
|
@ -22,7 +22,7 @@ New configuration options in taskwarrior 2.3.0
|
|||
|
||||
Newly deprecated features in taskwarrior 2.3.0
|
||||
|
||||
-
|
||||
- File format 2 (used in version 0.9.3 - 1.5.0) is no longer supported.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/* cmake.h.in. Creates cmake.h during a cmake run */
|
||||
|
||||
/* Product identification */
|
||||
#define PRODUCT_TASKWARRIOR 1
|
||||
|
||||
/* Package information */
|
||||
#define PACKAGE "${PACKAGE}"
|
||||
#define VERSION "${VERSION}"
|
||||
|
|
279
src/Task.cpp
279
src/Task.cpp
|
@ -25,21 +25,30 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cmake.h>
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
#include <sstream>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <Context.h>
|
||||
#include <Nibbler.h>
|
||||
#endif
|
||||
#include <Date.h>
|
||||
#include <Duration.h>
|
||||
#include <Task.h>
|
||||
#include <JSON.h>
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
#include <RX.h>
|
||||
#endif
|
||||
#include <text.h>
|
||||
#include <util.h>
|
||||
|
||||
#include <i18n.h>
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
#include <main.h>
|
||||
|
||||
#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery.
|
||||
|
@ -96,14 +105,17 @@ void initializeUrgencyCoefficients ()
|
|||
coefficients[*var] = context.config.getReal (*var);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Task::Task ()
|
||||
: id (0)
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
, urgency_value (0.0)
|
||||
, recalc_urgency (true)
|
||||
, is_blocked (false)
|
||||
, is_blocking (false)
|
||||
#endif
|
||||
, annotation_count (0)
|
||||
{
|
||||
}
|
||||
|
@ -120,13 +132,14 @@ Task& Task::operator= (const Task& other)
|
|||
if (this != &other)
|
||||
{
|
||||
std::map <std::string, std::string>::operator= (other);
|
||||
|
||||
id = other.id;
|
||||
#ifdef PRODUCT_TASKWARIROR
|
||||
urgency_value = other.urgency_value;
|
||||
recalc_urgency = other.recalc_urgency;
|
||||
is_blocked = other.is_blocked;
|
||||
is_blocking = other.is_blocking;
|
||||
annotation_count = other.annotation_count;
|
||||
#endif
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
@ -152,11 +165,13 @@ bool Task::operator== (const Task& other)
|
|||
Task::Task (const std::string& input)
|
||||
{
|
||||
id = 0;
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
urgency_value = 0.0;
|
||||
recalc_urgency = true;
|
||||
is_blocked = false;
|
||||
is_blocking = false;
|
||||
annotation_count = 0;
|
||||
#endif
|
||||
parse (input);
|
||||
}
|
||||
|
||||
|
@ -189,6 +204,7 @@ std::string Task::statusToText (Task::status s)
|
|||
return "pending";
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::setEntry ()
|
||||
{
|
||||
|
@ -218,6 +234,7 @@ void Task::setStart ()
|
|||
|
||||
recalc_urgency = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::setModified ()
|
||||
|
@ -258,6 +275,7 @@ const std::string Task::get (const std::string& name) const
|
|||
return "";
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const std::string& Task::get_ref (const std::string& name) const
|
||||
{
|
||||
|
@ -287,6 +305,7 @@ unsigned long Task::get_ulong (const std::string& name) const
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
time_t Task::get_date (const std::string& name) const
|
||||
|
@ -303,7 +322,9 @@ void Task::set (const std::string& name, const std::string& value)
|
|||
{
|
||||
(*this)[name] = value;
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
recalc_urgency = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -311,7 +332,9 @@ void Task::set (const std::string& name, int value)
|
|||
{
|
||||
(*this)[name] = format (value);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
recalc_urgency = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -321,7 +344,9 @@ void Task::remove (const std::string& name)
|
|||
if ((it = this->find (name)) != this->end ())
|
||||
this->erase (it);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
recalc_urgency = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -335,9 +360,12 @@ void Task::setStatus (Task::status status)
|
|||
{
|
||||
set ("status", statusToText (status));
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
recalc_urgency = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Task::is_due () const
|
||||
{
|
||||
|
@ -391,10 +419,13 @@ bool Task::is_overdue () const
|
|||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Attempt an FF4 parse first, using Task::parse, and in the event of an error
|
||||
// try a legacy parse (F3, FF2). Note that FF1 is no longer supported.
|
||||
// try a JSON parse, otherwise a legacy parse (FF3).
|
||||
//
|
||||
// Note that FF1 and FF2 are no longer supported.
|
||||
//
|
||||
// start --> [ --> Att --> ] --> end
|
||||
// ^ |
|
||||
|
@ -410,6 +441,7 @@ void Task::parse (const std::string& input)
|
|||
|
||||
try
|
||||
{
|
||||
// File format version 4, from 2009-5-16 - now, v1.7.1+
|
||||
clear ();
|
||||
|
||||
Nibbler n (copy);
|
||||
|
@ -451,88 +483,141 @@ void Task::parse (const std::string& input)
|
|||
if (remainder.length ())
|
||||
throw std::string (STRING_RECORD_JUNK_AT_EOL);
|
||||
}
|
||||
else if (input[0] == '{')
|
||||
parseJSON (input);
|
||||
else
|
||||
throw std::string (STRING_RECORD_NOT_FF4);
|
||||
}
|
||||
|
||||
catch (const std::string&)
|
||||
{
|
||||
legacyParse (copy);
|
||||
parseLegacy (copy);
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
recalc_urgency = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::parseJSON (const std::string& line)
|
||||
{
|
||||
// Parse the whole thing.
|
||||
json::value* root = json::parse (line);
|
||||
if (root->type () == json::j_object)
|
||||
{
|
||||
json::object* root_obj = (json::object*)root;
|
||||
|
||||
// For each object element...
|
||||
json_object_iter i;
|
||||
for (i = root_obj->_data.begin ();
|
||||
i != root_obj->_data.end ();
|
||||
++i)
|
||||
{
|
||||
// If the attribute is a recognized column.
|
||||
Column* col = context.columns[i->first];
|
||||
if (col)
|
||||
{
|
||||
// Any specified id is ignored.
|
||||
if (i->first == "id")
|
||||
;
|
||||
|
||||
// Urgency, if present, is ignored.
|
||||
else if (i->first == "urgency")
|
||||
;
|
||||
|
||||
// Dates are converted from ISO to epoch.
|
||||
else if (col->type () == "date")
|
||||
{
|
||||
Date d (unquoteText (i->second->dump ()));
|
||||
set (i->first, d.toEpochString ());
|
||||
}
|
||||
|
||||
// Tags are an array of JSON strings.
|
||||
else if (i->first == "tags")
|
||||
{
|
||||
json::array* tags = (json::array*)i->second;
|
||||
json_array_iter t;
|
||||
for (t = tags->_data.begin ();
|
||||
t != tags->_data.end ();
|
||||
++t)
|
||||
{
|
||||
json::string* tag = (json::string*)*t;
|
||||
addTag (tag->_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Other types are simply added.
|
||||
else
|
||||
set (i->first, unquoteText (i->second->dump ()));
|
||||
}
|
||||
|
||||
// UDA orphans and annotations do not have columns.
|
||||
else
|
||||
{
|
||||
// Annotations are an array of JSON objects with 'entry' and
|
||||
// 'description' values and must be converted.
|
||||
if (i->first == "annotations")
|
||||
{
|
||||
std::map <std::string, std::string> annos;
|
||||
|
||||
json::array* atts = (json::array*)i->second;
|
||||
json_array_iter annotations;
|
||||
for (annotations = atts->_data.begin ();
|
||||
annotations != atts->_data.end ();
|
||||
++annotations)
|
||||
{
|
||||
json::object* annotation = (json::object*)*annotations;
|
||||
json::string* when = (json::string*)annotation->_data["entry"];
|
||||
json::string* what = (json::string*)annotation->_data["description"];
|
||||
|
||||
if (! when)
|
||||
throw format (STRING_TASK_NO_ENTRY, line);
|
||||
|
||||
if (! what)
|
||||
throw format (STRING_TASK_NO_DESC, line);
|
||||
|
||||
std::string name = "annotation_" + Date (when->_data).toEpochString ();
|
||||
|
||||
annos.insert (std::make_pair (name, what->_data));
|
||||
}
|
||||
|
||||
setAnnotations (annos);
|
||||
}
|
||||
|
||||
// UDA Orphan - must be preserved.
|
||||
else
|
||||
{
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
std::stringstream message;
|
||||
message << "Task::parseJSON found orphan '"
|
||||
<< i->first
|
||||
<< "' with value '"
|
||||
<< i->second
|
||||
<< "' --> preserved\n";
|
||||
context.debug (message.str ());
|
||||
#endif
|
||||
set (i->first, unquoteText (i->second->dump ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Support FF2, FF3.
|
||||
// Thankfully FF1 is no longer supported.
|
||||
void Task::legacyParse (const std::string& line)
|
||||
void Task::parseLegacy (const std::string& line)
|
||||
{
|
||||
switch (determineVersion (line))
|
||||
{
|
||||
// File format version 1, from 2006.11.27 - 2007.12.31
|
||||
case 1:
|
||||
throw std::string (STRING_TASK_NO_FF1);
|
||||
break;
|
||||
// File format version 1, from 2006-11-27 - 2007-12-31, v0.x+ - v0.9.3
|
||||
case 1: throw std::string (STRING_TASK_NO_FF1);
|
||||
|
||||
// File format version 2, from 2008.1.1 - 2009.3.23
|
||||
case 2:
|
||||
{
|
||||
if (line.length () > 46) // ^.{36} . \[\] \[\] \n
|
||||
{
|
||||
set ("uuid", line.substr (0, 36));
|
||||
// File format version 2, from 2008-1-1 - 2009-3-23, v0.9.3 - v1.5.0
|
||||
case 2: throw std::string (STRING_TASK_NO_FF2);
|
||||
|
||||
Task::status status = line[37] == '+' ? completed
|
||||
: line[37] == 'X' ? deleted
|
||||
: line[37] == 'r' ? recurring
|
||||
: pending;
|
||||
|
||||
set ("status", statusToText (status));
|
||||
|
||||
size_t openTagBracket = line.find ("[");
|
||||
size_t closeTagBracket = line.find ("]", openTagBracket);
|
||||
if (openTagBracket != std::string::npos &&
|
||||
closeTagBracket != std::string::npos)
|
||||
{
|
||||
size_t openAttrBracket = line.find ("[", closeTagBracket);
|
||||
size_t closeAttrBracket = line.find ("]", openAttrBracket);
|
||||
if (openAttrBracket != std::string::npos &&
|
||||
closeAttrBracket != std::string::npos)
|
||||
{
|
||||
std::string tags = line.substr (
|
||||
openTagBracket + 1, closeTagBracket - openTagBracket - 1);
|
||||
std::vector <std::string> tagSet;
|
||||
split (tagSet, tags, ' ');
|
||||
addTags (tagSet);
|
||||
|
||||
std::string attributes = line.substr (
|
||||
openAttrBracket + 1, closeAttrBracket - openAttrBracket - 1);
|
||||
std::vector <std::string> pairs;
|
||||
split (pairs, attributes, ' ');
|
||||
for (size_t i = 0; i < pairs.size (); ++i)
|
||||
{
|
||||
std::vector <std::string> pair;
|
||||
split (pair, pairs[i], ':');
|
||||
if (pair.size () == 2)
|
||||
set (pair[0], pair[1]);
|
||||
}
|
||||
|
||||
set ("description", line.substr (closeAttrBracket + 2));
|
||||
}
|
||||
else
|
||||
throw std::string (STRING_TASK_PARSE_ATT_BRACK);
|
||||
}
|
||||
else
|
||||
throw std::string (STRING_TASK_PARSE_TAG_BRACK);
|
||||
}
|
||||
else
|
||||
throw std::string (STRING_TASK_PARSE_TOO_SHORT);
|
||||
|
||||
removeAnnotations ();
|
||||
}
|
||||
break;
|
||||
|
||||
// File format version 3, from 2009.3.23
|
||||
// File format version 3, from 2009-3-23 - 2009-05-16, v1.6.0 - v1.7.1
|
||||
case 3:
|
||||
{
|
||||
if (line.length () > 49) // ^.{36} . \[\] \[\] \[\] \n
|
||||
|
@ -640,7 +725,9 @@ void Task::legacyParse (const std::string& line)
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
recalc_urgency = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -669,14 +756,15 @@ std::string Task::composeF4 () const
|
|||
return ff4;
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string Task::composeJSON (bool include_id /*= false*/) const
|
||||
std::string Task::composeJSON (bool decorate /*= false*/) const
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "{";
|
||||
|
||||
// ID inclusion is optional, not recommended.
|
||||
if (include_id)
|
||||
if (decorate)
|
||||
out << "\"id\":" << id << ",";
|
||||
|
||||
// First the non-annotations.
|
||||
|
@ -765,6 +853,7 @@ std::string Task::composeJSON (bool include_id /*= false*/) const
|
|||
}
|
||||
|
||||
// Include urgency.
|
||||
if (decorate)
|
||||
out << ","
|
||||
<< "\"urgency\":\""
|
||||
<< urgency_c ()
|
||||
|
@ -780,31 +869,6 @@ bool Task::hasAnnotations () const
|
|||
return annotation_count ? true : false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::getAnnotations (std::map <std::string, std::string>& annotations) const
|
||||
{
|
||||
annotations.clear ();
|
||||
|
||||
Task::const_iterator ci;
|
||||
for (ci = this->begin (); ci != this->end (); ++ci)
|
||||
if (ci->first.substr (0, 11) == "annotation_")
|
||||
annotations.insert (*ci);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::setAnnotations (const std::map <std::string, std::string>& annotations)
|
||||
{
|
||||
// Erase old annotations.
|
||||
removeAnnotations ();
|
||||
|
||||
std::map <std::string, std::string>::const_iterator ci;
|
||||
for (ci = annotations.begin (); ci != annotations.end (); ++ci)
|
||||
this->insert (*ci);
|
||||
|
||||
annotation_count = annotations.size ();
|
||||
recalc_urgency = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// The timestamp is part of the name:
|
||||
// annotation_1234567890:"..."
|
||||
|
@ -847,6 +911,31 @@ void Task::removeAnnotations ()
|
|||
recalc_urgency = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::getAnnotations (std::map <std::string, std::string>& annotations) const
|
||||
{
|
||||
annotations.clear ();
|
||||
|
||||
Task::const_iterator ci;
|
||||
for (ci = this->begin (); ci != this->end (); ++ci)
|
||||
if (ci->first.substr (0, 11) == "annotation_")
|
||||
annotations.insert (*ci);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::setAnnotations (const std::map <std::string, std::string>& annotations)
|
||||
{
|
||||
// Erase old annotations.
|
||||
removeAnnotations ();
|
||||
|
||||
std::map <std::string, std::string>::const_iterator ci;
|
||||
for (ci = annotations.begin (); ci != annotations.end (); ++ci)
|
||||
this->insert (*ci);
|
||||
|
||||
annotation_count = annotations.size ();
|
||||
recalc_urgency = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::addDependency (int id)
|
||||
{
|
||||
|
@ -1189,6 +1278,7 @@ void Task::substitute (
|
|||
recalc_urgency = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// The purpose of Task::validate is three-fold:
|
||||
|
@ -1224,6 +1314,7 @@ void Task::validate (bool applyDefault /* = true */)
|
|||
// Store the derived status.
|
||||
setStatus (status);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
// Provide an entry date unless user already specified one.
|
||||
if (!has ("entry"))
|
||||
setEntry ();
|
||||
|
@ -1309,6 +1400,7 @@ void Task::validate (bool applyDefault /* = true */)
|
|||
validate_before ("scheduled", "start");
|
||||
validate_before ("scheduled", "due");
|
||||
validate_before ("scheduled", "end");
|
||||
#endif
|
||||
|
||||
// 3) To generate errors when the inconsistencies are not fixable
|
||||
|
||||
|
@ -1341,6 +1433,8 @@ void Task::validate (bool applyDefault /* = true */)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Task::validate_before (const std::string& left, const std::string& right)
|
||||
{
|
||||
if (has (left) &&
|
||||
|
@ -1674,5 +1768,6 @@ float Task::urgency_blocking () const
|
|||
|
||||
return 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
29
src/Task.h
29
src/Task.h
|
@ -28,13 +28,15 @@
|
|||
#ifndef INCLUDED_TASK
|
||||
#define INCLUDED_TASK
|
||||
|
||||
#include <cmake.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
void initializeUrgencyCoefficients ();
|
||||
#endif
|
||||
|
||||
class Task : public std::map <std::string, std::string>
|
||||
{
|
||||
|
@ -48,18 +50,22 @@ public:
|
|||
|
||||
void parse (const std::string&);
|
||||
std::string composeF4 () const;
|
||||
std::string composeJSON (bool include_id = false) const;
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
std::string composeJSON (bool decorate = false) const;
|
||||
#endif
|
||||
|
||||
// Status values.
|
||||
enum status {pending, completed, deleted, recurring, waiting};
|
||||
|
||||
// Public data.
|
||||
int id;
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
float urgency_value;
|
||||
bool recalc_urgency;
|
||||
|
||||
bool is_blocked;
|
||||
bool is_blocking;
|
||||
#endif
|
||||
|
||||
int annotation_count;
|
||||
|
||||
|
@ -67,39 +73,50 @@ public:
|
|||
static status textToStatus (const std::string&);
|
||||
static std::string statusToText (status);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
void setEntry ();
|
||||
void setEnd ();
|
||||
void setStart ();
|
||||
#endif
|
||||
void setModified ();
|
||||
|
||||
bool has (const std::string&) const;
|
||||
std::vector <std::string> all ();
|
||||
const std::string get (const std::string&) const;
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
const std::string& get_ref (const std::string&) const;
|
||||
int get_int (const std::string&) const;
|
||||
unsigned long get_ulong (const std::string&) const;
|
||||
#endif
|
||||
time_t get_date (const std::string&) const;
|
||||
void set (const std::string&, const std::string&);
|
||||
void set (const std::string&, int);
|
||||
void remove (const std::string&);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
bool is_due () const;
|
||||
bool is_duetoday () const;
|
||||
bool is_overdue () const;
|
||||
#endif
|
||||
|
||||
status getStatus () const;
|
||||
void setStatus (status);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
int getTagCount () const;
|
||||
bool hasTag (const std::string&) const;
|
||||
#endif
|
||||
void addTag (const std::string&);
|
||||
void addTags (const std::vector <std::string>&);
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
void getTags (std::vector<std::string>&) const;
|
||||
void removeTag (const std::string&);
|
||||
|
||||
bool hasAnnotations () const;
|
||||
void getAnnotations (std::map <std::string, std::string>&) const;
|
||||
#endif
|
||||
void setAnnotations (const std::map <std::string, std::string>&);
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
void addAnnotation (const std::string&);
|
||||
void removeAnnotations ();
|
||||
|
||||
|
@ -114,15 +131,20 @@ public:
|
|||
void getUDAOrphans (std::vector <std::string>&) const;
|
||||
|
||||
void substitute (const std::string&, const std::string&, bool);
|
||||
#endif
|
||||
|
||||
void validate (bool applyDefault = true);
|
||||
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
float urgency_c () const;
|
||||
float urgency ();
|
||||
#endif
|
||||
|
||||
private:
|
||||
int determineVersion (const std::string&);
|
||||
void legacyParse (const std::string&);
|
||||
void parseJSON (const std::string&);
|
||||
void parseLegacy (const std::string&);
|
||||
#ifdef PRODUCT_TASKWARRIOR
|
||||
void validate_before (const std::string&, const std::string&);
|
||||
|
||||
inline float urgency_priority () const;
|
||||
|
@ -137,6 +159,7 @@ private:
|
|||
inline float urgency_due () const;
|
||||
inline float urgency_blocking () const;
|
||||
inline float urgency_age () const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -172,10 +172,10 @@ int CmdImport::execute (std::string& output)
|
|||
json::string* what = (json::string*)annotation->_data["description"];
|
||||
|
||||
if (! when)
|
||||
throw format (STRING_CMD_IMPORT_NO_ENTRY, *line);
|
||||
throw format (STRING_TASK_NO_ENTRY, *line);
|
||||
|
||||
if (! what)
|
||||
throw format (STRING_CMD_IMPORT_NO_DESC, *line);
|
||||
throw format (STRING_TASK_NO_DESC, *line);
|
||||
|
||||
std::string name = "annotation_" + Date (when->_data).toEpochString ();
|
||||
|
||||
|
|
|
@ -396,8 +396,8 @@
|
|||
#define STRING_CMD_IMPORT_NOFILE "You must specify a file to import."
|
||||
#define STRING_CMD_IMPORT_FILE "Importing '{1}'"
|
||||
#define STRING_CMD_IMPORT_NOT_JSON "Not a JSON object: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_DESC "Annotation is missing a description: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_ENTRY "Annotation is missing an entry date: {1}"
|
||||
#define STRING_TASK_NO_DESC "Annotation is missing a description: {1}"
|
||||
#define STRING_TASK_NO_ENTRY "Annotation is missing an entry date: {1}"
|
||||
#define STRING_CMD_SHELL_HELP1 "Enter any task command (such as 'list'), or hit 'Enter'."
|
||||
#define STRING_CMD_SHELL_HELP2 "There is no need to include the 'task' command itself."
|
||||
#define STRING_CMD_SHELL_HELP3 "Enter 'quit' (or 'bye', 'exit') to end the session."
|
||||
|
@ -779,6 +779,7 @@
|
|||
|
||||
// Task
|
||||
#define STRING_TASK_NO_FF1 "Taskwarrior no longer supports file format 1, originally used between 27 November 2006 and 31 December 2007."
|
||||
#define STRING_TASK_NO_FF2 "Taskwarrior no longer supports file format 2, originally used between 1 January 2008 and 12 April 2009."
|
||||
#define STRING_TASK_PARSE_ANNO_BRACK "Missing annotation brackets."
|
||||
#define STRING_TASK_PARSE_ATT_BRACK "Missing attribute brackets."
|
||||
#define STRING_TASK_PARSE_TAG_BRACK "Missing tag brackets."
|
||||
|
|
|
@ -406,8 +406,8 @@
|
|||
#define STRING_CMD_IMPORT_NOFILE "Debe especificar un archivo a importar."
|
||||
#define STRING_CMD_IMPORT_FILE "Importando '{1}'"
|
||||
#define STRING_CMD_IMPORT_NOT_JSON "No es un objeto JSON: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_DESC "La anotación carece de descripción: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_ENTRY "La anotación carece de fecha de entrada: {1}"
|
||||
#define STRING_TASK_NO_DESC "La anotación carece de descripción: {1}"
|
||||
#define STRING_TASK_NO_ENTRY "La anotación carece de fecha de entrada: {1}"
|
||||
#define STRING_CMD_SHELL_HELP1 "Escriba un comando de task (como 'list'), o pulse 'Enter'."
|
||||
#define STRING_CMD_SHELL_HELP2 "No hace falta incluir el propio comando 'task'."
|
||||
#define STRING_CMD_SHELL_HELP3 "Escriba 'quit' (o 'bye', 'exit') para terminar la sesión."
|
||||
|
@ -794,6 +794,7 @@
|
|||
|
||||
// Task
|
||||
#define STRING_TASK_NO_FF1 "Taskwarrior ya no admite el formato de archivo 1, usado originalmente entre el 27 de noviembre del 2006 y el 31 de diciembre del 2007."
|
||||
#define STRING_TASK_NO_FF2 "Taskwarrior no longer supports file format 2, originally used between 1 January 2008 and 12 April 2009."
|
||||
#define STRING_TASK_PARSE_ANNO_BRACK "Faltan corchetes de anotación."
|
||||
#define STRING_TASK_PARSE_ATT_BRACK "Faltan corchetes de atributo."
|
||||
#define STRING_TASK_PARSE_TAG_BRACK "Faltan corchetes de marca."
|
||||
|
|
|
@ -396,8 +396,8 @@
|
|||
#define STRING_CMD_IMPORT_NOFILE "You must specify a file to import."
|
||||
#define STRING_CMD_IMPORT_FILE "Importing '{1}'"
|
||||
#define STRING_CMD_IMPORT_NOT_JSON "Not a JSON object: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_DESC "Annotation is missing a description: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_ENTRY "Annotation is missing an entry date: {1}"
|
||||
#define STRING_TASK_NO_DESC "Annotation is missing a description: {1}"
|
||||
#define STRING_TASK_NO_ENTRY "Annotation is missing an entry date: {1}"
|
||||
#define STRING_CMD_SHELL_HELP1 "Enter any task command (such as 'list'), or hit 'Enter'."
|
||||
#define STRING_CMD_SHELL_HELP2 "There is no need to include the 'task' command itself."
|
||||
#define STRING_CMD_SHELL_HELP3 "Enter 'quit' (or 'bye', 'exit') to end the session."
|
||||
|
@ -779,6 +779,7 @@
|
|||
|
||||
// Task
|
||||
#define STRING_TASK_NO_FF1 "Taskwarrior no longer supports file format 1, originally used between 27 November 2006 and 31 December 2007."
|
||||
#define STRING_TASK_NO_FF2 "Taskwarrior no longer supports file format 2, originally used between 1 January 2008 and 12 April 2009."
|
||||
#define STRING_TASK_PARSE_ANNO_BRACK "Missing annotation brackets."
|
||||
#define STRING_TASK_PARSE_ATT_BRACK "Missing attribute brackets."
|
||||
#define STRING_TASK_PARSE_TAG_BRACK "Missing tag brackets."
|
||||
|
|
|
@ -397,8 +397,8 @@
|
|||
#define STRING_CMD_IMPORT_NOFILE "Specificare il file da importare."
|
||||
#define STRING_CMD_IMPORT_FILE "Importazione di '{1}'"
|
||||
#define STRING_CMD_IMPORT_NOT_JSON "Non è un oggetto JSON: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_DESC "Annotazione senza descrizione: {1}"
|
||||
#define STRING_CMD_IMPORT_NO_ENTRY "Annotazione senza data di immissione: {1}"
|
||||
#define STRING_TASK_NO_DESC "Annotazione senza descrizione: {1}"
|
||||
#define STRING_TASK_NO_ENTRY "Annotazione senza data di immissione: {1}"
|
||||
#define STRING_CMD_SHELL_HELP1 "Inserisci un comando (es. 'list'), o premi 'Enter'."
|
||||
#define STRING_CMD_SHELL_HELP2 "Non è necessario includere il comando 'task'."
|
||||
#define STRING_CMD_SHELL_HELP3 "Inserisci 'quit' (o 'bye', 'exit') per terminare la sessione."
|
||||
|
@ -780,6 +780,7 @@
|
|||
|
||||
// Task
|
||||
#define STRING_TASK_NO_FF1 "Taskwarrior non supporta più il formato di file 1, usato tra il 27 Novembre 2006 e il 31 Dicembre 2007."
|
||||
#define STRING_TASK_NO_FF2 "Taskwarrior no longer supports file format 2, originally used between 1 January 2008 and 12 April 2009."
|
||||
#define STRING_TASK_PARSE_ANNO_BRACK "Parentesi di annotazione mancanti."
|
||||
#define STRING_TASK_PARSE_ATT_BRACK "Parentesi di attributo mancanti."
|
||||
#define STRING_TASK_PARSE_TAG_BRACK "Parentesi di tag mancanti."
|
||||
|
|
47
test/t.t.cpp
47
test/t.t.cpp
|
@ -33,7 +33,7 @@ Context context;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest test (37);
|
||||
UnitTest test (30);
|
||||
|
||||
test.is ((int)Task::textToStatus ("pending"), (int)Task::pending, "textToStatus pending");
|
||||
test.is ((int)Task::textToStatus ("completed"), (int)Task::completed, "textToStatus completed");
|
||||
|
@ -57,7 +57,7 @@ int main (int argc, char** argv)
|
|||
after = t3.composeF4 ();
|
||||
test.is (before, after, "Task::composeF4 -> parse round trip 4 iterations");
|
||||
|
||||
// Legacy Format 1
|
||||
// Legacy Format 1 (no longer supported)
|
||||
// [tags] [attributes] description\n
|
||||
// X [tags] [attributes] description\n
|
||||
std::string sample = "[tag1 tag2] [att1:value1 att2:value2] Description";
|
||||
|
@ -69,27 +69,16 @@ int main (int argc, char** argv)
|
|||
try { Task ff1 (sample); } catch (...) { good = false; }
|
||||
test.notok (good, "Support for ff1 removed");
|
||||
|
||||
// Legacy Format 2
|
||||
// Legacy Format 2 (no longer supported)
|
||||
// uuid status [tags] [attributes] description\n
|
||||
sample = "00000000-0000-0000-0000-000000000000 "
|
||||
"- "
|
||||
"[tag1 tag2] "
|
||||
"[att1:value1 att2:value2] "
|
||||
"Description";
|
||||
Task ff2 (sample);
|
||||
std::string value = ff2.get ("uuid");
|
||||
test.is (value, "00000000-0000-0000-0000-000000000000", "ff2 uuid");
|
||||
value = ff2.get ("status");
|
||||
test.is (value, "pending", "ff2 status");
|
||||
test.ok (ff2.hasTag ("tag1"), "ff2 tag1");
|
||||
test.ok (ff2.hasTag ("tag2"), "ff2 tag2");
|
||||
test.is (ff2.getTagCount (), 2, "ff2 # tags");
|
||||
value = ff2.get ("att1");
|
||||
test.is (value, "value1", "ff2 att1");
|
||||
value = ff2.get ("att2");
|
||||
test.is (value, "value2", "ff2 att2");
|
||||
value = ff2.get ("description");
|
||||
test.is (value, "Description", "ff2 description");
|
||||
good = true;
|
||||
try { Task ff2 (sample); } catch (...) { good = false; }
|
||||
test.notok (good, "Support for ff2 removed");
|
||||
|
||||
// Legacy Format 3
|
||||
// uuid status [tags] [attributes] [annotations] description\n
|
||||
|
@ -99,13 +88,13 @@ int main (int argc, char** argv)
|
|||
"[att1:value1 att2:value2] "
|
||||
"[123:ann1 456:ann2] Description";
|
||||
Task ff3 (sample);
|
||||
value = ff2.get ("uuid");
|
||||
std::string value = ff3.get ("uuid");
|
||||
test.is (value, "00000000-0000-0000-0000-000000000000", "ff3 uuid");
|
||||
value = ff2.get ("status");
|
||||
value = ff3.get ("status");
|
||||
test.is (value, "pending", "ff3 status");
|
||||
test.ok (ff2.hasTag ("tag1"), "ff3 tag1");
|
||||
test.ok (ff2.hasTag ("tag2"), "ff3 tag2");
|
||||
test.is (ff2.getTagCount (), 2, "ff3 # tags");
|
||||
test.ok (ff3.hasTag ("tag1"), "ff3 tag1");
|
||||
test.ok (ff3.hasTag ("tag2"), "ff3 tag2");
|
||||
test.is (ff3.getTagCount (), 2, "ff3 # tags");
|
||||
value = ff3.get ("att1");
|
||||
test.is (value, "value1", "ff3 att1");
|
||||
value = ff3.get ("att2");
|
||||
|
@ -117,20 +106,20 @@ int main (int argc, char** argv)
|
|||
// [name:"value" ...]\n
|
||||
sample = "["
|
||||
"uuid:\"00000000-0000-0000-0000-000000000000\" "
|
||||
"status:\"P\" "
|
||||
"tags:\"tag1&commaltag2\" "
|
||||
"status:\"pending\" "
|
||||
"tags:\"tag1,tag2\" "
|
||||
"att1:\"value1\" "
|
||||
"att2:\"value2\" "
|
||||
"description:\"Description\""
|
||||
"]";
|
||||
Task ff4 (sample);
|
||||
value = ff2.get ("uuid");
|
||||
value = ff4.get ("uuid");
|
||||
test.is (value, "00000000-0000-0000-0000-000000000000", "ff4 uuid");
|
||||
value = ff2.get ("status");
|
||||
value = ff4.get ("status");
|
||||
test.is (value, "pending", "ff4 status");
|
||||
test.ok (ff2.hasTag ("tag1"), "ff4 tag1");
|
||||
test.ok (ff2.hasTag ("tag2"), "ff4 tag2");
|
||||
test.is (ff2.getTagCount (), 2, "ff4 # tags");
|
||||
test.ok (ff4.hasTag ("tag1"), "ff4 tag1");
|
||||
test.ok (ff4.hasTag ("tag2"), "ff4 tag2");
|
||||
test.is (ff4.getTagCount (), 2, "ff4 # tags");
|
||||
value = ff4.get ("att1");
|
||||
test.is (value, "value1", "ff4 att1");
|
||||
value = ff4.get ("att2");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue