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:
Paul Beckingham 2013-05-20 16:03:45 -04:00
parent 00f8f56c00
commit 6a97017c79
11 changed files with 254 additions and 139 deletions

View file

@ -24,6 +24,7 @@ Features
+ The 'due' urgency component now uses seconds, not days, in the calculation. + The 'due' urgency component now uses seconds, not days, in the calculation.
+ The 'debug.tls' configuration variable takes an integer which corresponds to + The 'debug.tls' configuration variable takes an integer which corresponds to
the GnuTLS log level. For debugging. the GnuTLS log level. For debugging.
+ File format 2 (used in version 0.9.3 - 1.5.0) is no longer supported.
Bugs Bugs
+ #1196 Now builds on Hurd (thanks to Jakub Wilk). + #1196 Now builds on Hurd (thanks to Jakub Wilk).

2
NEWS
View file

@ -22,7 +22,7 @@ New configuration options in taskwarrior 2.3.0
Newly deprecated features 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.
--- ---

View file

@ -1,5 +1,8 @@
/* cmake.h.in. Creates cmake.h during a cmake run */ /* cmake.h.in. Creates cmake.h during a cmake run */
/* Product identification */
#define PRODUCT_TASKWARRIOR 1
/* Package information */ /* Package information */
#define PACKAGE "${PACKAGE}" #define PACKAGE "${PACKAGE}"
#define VERSION "${VERSION}" #define VERSION "${VERSION}"

View file

@ -25,21 +25,30 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#ifdef PRODUCT_TASKWARRIOR
#include <sstream> #include <sstream>
#endif
#include <stdlib.h> #include <stdlib.h>
#ifdef PRODUCT_TASKWARRIOR
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include <algorithm> #include <algorithm>
#include <Context.h> #include <Context.h>
#include <Nibbler.h> #include <Nibbler.h>
#endif
#include <Date.h> #include <Date.h>
#include <Duration.h> #include <Duration.h>
#include <Task.h> #include <Task.h>
#include <JSON.h> #include <JSON.h>
#ifdef PRODUCT_TASKWARRIOR
#include <RX.h> #include <RX.h>
#endif
#include <text.h> #include <text.h>
#include <util.h> #include <util.h>
#include <i18n.h> #include <i18n.h>
#ifdef PRODUCT_TASKWARRIOR
#include <main.h> #include <main.h>
#define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery. #define APPROACHING_INFINITY 1000 // Close enough. This isn't rocket surgery.
@ -96,14 +105,17 @@ void initializeUrgencyCoefficients ()
coefficients[*var] = context.config.getReal (*var); coefficients[*var] = context.config.getReal (*var);
} }
} }
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Task::Task () Task::Task ()
: id (0) : id (0)
#ifdef PRODUCT_TASKWARRIOR
, urgency_value (0.0) , urgency_value (0.0)
, recalc_urgency (true) , recalc_urgency (true)
, is_blocked (false) , is_blocked (false)
, is_blocking (false) , is_blocking (false)
#endif
, annotation_count (0) , annotation_count (0)
{ {
} }
@ -120,13 +132,14 @@ Task& Task::operator= (const Task& other)
if (this != &other) if (this != &other)
{ {
std::map <std::string, std::string>::operator= (other); std::map <std::string, std::string>::operator= (other);
id = other.id; id = other.id;
#ifdef PRODUCT_TASKWARIROR
urgency_value = other.urgency_value; urgency_value = other.urgency_value;
recalc_urgency = other.recalc_urgency; recalc_urgency = other.recalc_urgency;
is_blocked = other.is_blocked; is_blocked = other.is_blocked;
is_blocking = other.is_blocking; is_blocking = other.is_blocking;
annotation_count = other.annotation_count; annotation_count = other.annotation_count;
#endif
} }
return *this; return *this;
@ -152,11 +165,13 @@ bool Task::operator== (const Task& other)
Task::Task (const std::string& input) Task::Task (const std::string& input)
{ {
id = 0; id = 0;
#ifdef PRODUCT_TASKWARRIOR
urgency_value = 0.0; urgency_value = 0.0;
recalc_urgency = true; recalc_urgency = true;
is_blocked = false; is_blocked = false;
is_blocking = false; is_blocking = false;
annotation_count = 0; annotation_count = 0;
#endif
parse (input); parse (input);
} }
@ -189,6 +204,7 @@ std::string Task::statusToText (Task::status s)
return "pending"; return "pending";
} }
#ifdef PRODUCT_TASKWARRIOR
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Task::setEntry () void Task::setEntry ()
{ {
@ -218,6 +234,7 @@ void Task::setStart ()
recalc_urgency = true; recalc_urgency = true;
} }
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Task::setModified () void Task::setModified ()
@ -258,6 +275,7 @@ const std::string Task::get (const std::string& name) const
return ""; return "";
} }
#ifdef PRODUCT_TASKWARRIOR
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const std::string& Task::get_ref (const std::string& name) const 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; return 0;
} }
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
time_t Task::get_date (const std::string& name) const 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; (*this)[name] = value;
#ifdef PRODUCT_TASKWARRIOR
recalc_urgency = true; recalc_urgency = true;
#endif
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -311,7 +332,9 @@ void Task::set (const std::string& name, int value)
{ {
(*this)[name] = format (value); (*this)[name] = format (value);
#ifdef PRODUCT_TASKWARRIOR
recalc_urgency = true; recalc_urgency = true;
#endif
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -321,7 +344,9 @@ void Task::remove (const std::string& name)
if ((it = this->find (name)) != this->end ()) if ((it = this->find (name)) != this->end ())
this->erase (it); this->erase (it);
#ifdef PRODUCT_TASKWARRIOR
recalc_urgency = true; recalc_urgency = true;
#endif
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -335,9 +360,12 @@ void Task::setStatus (Task::status status)
{ {
set ("status", statusToText (status)); set ("status", statusToText (status));
#ifdef PRODUCT_TASKWARRIOR
recalc_urgency = true; recalc_urgency = true;
#endif
} }
#ifdef PRODUCT_TASKWARRIOR
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool Task::is_due () const bool Task::is_due () const
{ {
@ -391,10 +419,13 @@ bool Task::is_overdue () const
return false; return false;
} }
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Attempt an FF4 parse first, using Task::parse, and in the event of an error // 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 // start --> [ --> Att --> ] --> end
// ^ | // ^ |
@ -410,6 +441,7 @@ void Task::parse (const std::string& input)
try try
{ {
// File format version 4, from 2009-5-16 - now, v1.7.1+
clear (); clear ();
Nibbler n (copy); Nibbler n (copy);
@ -451,88 +483,141 @@ void Task::parse (const std::string& input)
if (remainder.length ()) if (remainder.length ())
throw std::string (STRING_RECORD_JUNK_AT_EOL); throw std::string (STRING_RECORD_JUNK_AT_EOL);
} }
else if (input[0] == '{')
parseJSON (input);
else else
throw std::string (STRING_RECORD_NOT_FF4); throw std::string (STRING_RECORD_NOT_FF4);
} }
catch (const std::string&) catch (const std::string&)
{ {
legacyParse (copy); parseLegacy (copy);
} }
#ifdef PRODUCT_TASKWARRIOR
recalc_urgency = true; 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. // Support FF2, FF3.
// Thankfully FF1 is no longer supported. // Thankfully FF1 is no longer supported.
void Task::legacyParse (const std::string& line) void Task::parseLegacy (const std::string& line)
{ {
switch (determineVersion (line)) switch (determineVersion (line))
{ {
// File format version 1, from 2006.11.27 - 2007.12.31 // File format version 1, from 2006-11-27 - 2007-12-31, v0.x+ - v0.9.3
case 1: case 1: throw std::string (STRING_TASK_NO_FF1);
throw std::string (STRING_TASK_NO_FF1);
break;
// File format version 2, from 2008.1.1 - 2009.3.23 // File format version 2, from 2008-1-1 - 2009-3-23, v0.9.3 - v1.5.0
case 2: case 2: throw std::string (STRING_TASK_NO_FF2);
{
if (line.length () > 46) // ^.{36} . \[\] \[\] \n
{
set ("uuid", line.substr (0, 36));
Task::status status = line[37] == '+' ? completed // File format version 3, from 2009-3-23 - 2009-05-16, v1.6.0 - v1.7.1
: 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
case 3: case 3:
{ {
if (line.length () > 49) // ^.{36} . \[\] \[\] \[\] \n if (line.length () > 49) // ^.{36} . \[\] \[\] \[\] \n
@ -640,7 +725,9 @@ void Task::legacyParse (const std::string& line)
break; break;
} }
#ifdef PRODUCT_TASKWARRIOR
recalc_urgency = true; recalc_urgency = true;
#endif
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -669,14 +756,15 @@ std::string Task::composeF4 () const
return ff4; 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; std::stringstream out;
out << "{"; out << "{";
// ID inclusion is optional, not recommended. // ID inclusion is optional, not recommended.
if (include_id) if (decorate)
out << "\"id\":" << id << ","; out << "\"id\":" << id << ",";
// First the non-annotations. // First the non-annotations.
@ -765,6 +853,7 @@ std::string Task::composeJSON (bool include_id /*= false*/) const
} }
// Include urgency. // Include urgency.
if (decorate)
out << "," out << ","
<< "\"urgency\":\"" << "\"urgency\":\""
<< urgency_c () << urgency_c ()
@ -780,31 +869,6 @@ bool Task::hasAnnotations () const
return annotation_count ? true : false; 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: // The timestamp is part of the name:
// annotation_1234567890:"..." // annotation_1234567890:"..."
@ -847,6 +911,31 @@ void Task::removeAnnotations ()
recalc_urgency = true; 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) void Task::addDependency (int id)
{ {
@ -1189,6 +1278,7 @@ void Task::substitute (
recalc_urgency = true; recalc_urgency = true;
} }
} }
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// The purpose of Task::validate is three-fold: // The purpose of Task::validate is three-fold:
@ -1224,6 +1314,7 @@ void Task::validate (bool applyDefault /* = true */)
// Store the derived status. // Store the derived status.
setStatus (status); setStatus (status);
#ifdef PRODUCT_TASKWARRIOR
// Provide an entry date unless user already specified one. // Provide an entry date unless user already specified one.
if (!has ("entry")) if (!has ("entry"))
setEntry (); setEntry ();
@ -1309,6 +1400,7 @@ void Task::validate (bool applyDefault /* = true */)
validate_before ("scheduled", "start"); validate_before ("scheduled", "start");
validate_before ("scheduled", "due"); validate_before ("scheduled", "due");
validate_before ("scheduled", "end"); validate_before ("scheduled", "end");
#endif
// 3) To generate errors when the inconsistencies are not fixable // 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) void Task::validate_before (const std::string& left, const std::string& right)
{ {
if (has (left) && if (has (left) &&
@ -1674,5 +1768,6 @@ float Task::urgency_blocking () const
return 0.0; return 0.0;
} }
#endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -28,13 +28,15 @@
#ifndef INCLUDED_TASK #ifndef INCLUDED_TASK
#define INCLUDED_TASK #define INCLUDED_TASK
#include <cmake.h>
#include <vector> #include <vector>
#include <map> #include <map>
#include <string> #include <string>
#include <stdio.h> #include <stdio.h>
#ifdef PRODUCT_TASKWARRIOR
void initializeUrgencyCoefficients (); void initializeUrgencyCoefficients ();
#endif
class Task : public std::map <std::string, std::string> class Task : public std::map <std::string, std::string>
{ {
@ -48,18 +50,22 @@ public:
void parse (const std::string&); void parse (const std::string&);
std::string composeF4 () const; 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. // Status values.
enum status {pending, completed, deleted, recurring, waiting}; enum status {pending, completed, deleted, recurring, waiting};
// Public data. // Public data.
int id; int id;
#ifdef PRODUCT_TASKWARRIOR
float urgency_value; float urgency_value;
bool recalc_urgency; bool recalc_urgency;
bool is_blocked; bool is_blocked;
bool is_blocking; bool is_blocking;
#endif
int annotation_count; int annotation_count;
@ -67,39 +73,50 @@ public:
static status textToStatus (const std::string&); static status textToStatus (const std::string&);
static std::string statusToText (status); static std::string statusToText (status);
#ifdef PRODUCT_TASKWARRIOR
void setEntry (); void setEntry ();
void setEnd (); void setEnd ();
void setStart (); void setStart ();
#endif
void setModified (); void setModified ();
bool has (const std::string&) const; bool has (const std::string&) const;
std::vector <std::string> all (); std::vector <std::string> all ();
const std::string get (const std::string&) const; const std::string get (const std::string&) const;
#ifdef PRODUCT_TASKWARRIOR
const std::string& get_ref (const std::string&) const; const std::string& get_ref (const std::string&) const;
int get_int (const std::string&) const; int get_int (const std::string&) const;
unsigned long get_ulong (const std::string&) const; unsigned long get_ulong (const std::string&) const;
#endif
time_t get_date (const std::string&) const; time_t get_date (const std::string&) const;
void set (const std::string&, const std::string&); void set (const std::string&, const std::string&);
void set (const std::string&, int); void set (const std::string&, int);
void remove (const std::string&); void remove (const std::string&);
#ifdef PRODUCT_TASKWARRIOR
bool is_due () const; bool is_due () const;
bool is_duetoday () const; bool is_duetoday () const;
bool is_overdue () const; bool is_overdue () const;
#endif
status getStatus () const; status getStatus () const;
void setStatus (status); void setStatus (status);
#ifdef PRODUCT_TASKWARRIOR
int getTagCount () const; int getTagCount () const;
bool hasTag (const std::string&) const; bool hasTag (const std::string&) const;
#endif
void addTag (const std::string&); void addTag (const std::string&);
void addTags (const std::vector <std::string>&); void addTags (const std::vector <std::string>&);
#ifdef PRODUCT_TASKWARRIOR
void getTags (std::vector<std::string>&) const; void getTags (std::vector<std::string>&) const;
void removeTag (const std::string&); void removeTag (const std::string&);
bool hasAnnotations () const; bool hasAnnotations () const;
void getAnnotations (std::map <std::string, std::string>&) const; void getAnnotations (std::map <std::string, std::string>&) const;
#endif
void setAnnotations (const std::map <std::string, std::string>&); void setAnnotations (const std::map <std::string, std::string>&);
#ifdef PRODUCT_TASKWARRIOR
void addAnnotation (const std::string&); void addAnnotation (const std::string&);
void removeAnnotations (); void removeAnnotations ();
@ -114,15 +131,20 @@ public:
void getUDAOrphans (std::vector <std::string>&) const; void getUDAOrphans (std::vector <std::string>&) const;
void substitute (const std::string&, const std::string&, bool); void substitute (const std::string&, const std::string&, bool);
#endif
void validate (bool applyDefault = true); void validate (bool applyDefault = true);
#ifdef PRODUCT_TASKWARRIOR
float urgency_c () const; float urgency_c () const;
float urgency (); float urgency ();
#endif
private: private:
int determineVersion (const std::string&); 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&); void validate_before (const std::string&, const std::string&);
inline float urgency_priority () const; inline float urgency_priority () const;
@ -137,6 +159,7 @@ private:
inline float urgency_due () const; inline float urgency_due () const;
inline float urgency_blocking () const; inline float urgency_blocking () const;
inline float urgency_age () const; inline float urgency_age () const;
#endif
}; };
#endif #endif

View file

@ -172,10 +172,10 @@ int CmdImport::execute (std::string& output)
json::string* what = (json::string*)annotation->_data["description"]; json::string* what = (json::string*)annotation->_data["description"];
if (! when) if (! when)
throw format (STRING_CMD_IMPORT_NO_ENTRY, *line); throw format (STRING_TASK_NO_ENTRY, *line);
if (! what) 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 (); std::string name = "annotation_" + Date (when->_data).toEpochString ();

View file

@ -396,8 +396,8 @@
#define STRING_CMD_IMPORT_NOFILE "You must specify a file to import." #define STRING_CMD_IMPORT_NOFILE "You must specify a file to import."
#define STRING_CMD_IMPORT_FILE "Importing '{1}'" #define STRING_CMD_IMPORT_FILE "Importing '{1}'"
#define STRING_CMD_IMPORT_NOT_JSON "Not a JSON object: {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_TASK_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_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_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_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." #define STRING_CMD_SHELL_HELP3 "Enter 'quit' (or 'bye', 'exit') to end the session."
@ -779,6 +779,7 @@
// Task // 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_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_ANNO_BRACK "Missing annotation brackets."
#define STRING_TASK_PARSE_ATT_BRACK "Missing attribute brackets." #define STRING_TASK_PARSE_ATT_BRACK "Missing attribute brackets."
#define STRING_TASK_PARSE_TAG_BRACK "Missing tag brackets." #define STRING_TASK_PARSE_TAG_BRACK "Missing tag brackets."

View file

@ -406,8 +406,8 @@
#define STRING_CMD_IMPORT_NOFILE "Debe especificar un archivo a importar." #define STRING_CMD_IMPORT_NOFILE "Debe especificar un archivo a importar."
#define STRING_CMD_IMPORT_FILE "Importando '{1}'" #define STRING_CMD_IMPORT_FILE "Importando '{1}'"
#define STRING_CMD_IMPORT_NOT_JSON "No es un objeto JSON: {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_TASK_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_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_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_HELP2 "No hace falta incluir el propio comando 'task'."
#define STRING_CMD_SHELL_HELP3 "Escriba 'quit' (o 'bye', 'exit') para terminar la sesión." #define STRING_CMD_SHELL_HELP3 "Escriba 'quit' (o 'bye', 'exit') para terminar la sesión."
@ -794,6 +794,7 @@
// Task // 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_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_ANNO_BRACK "Faltan corchetes de anotación."
#define STRING_TASK_PARSE_ATT_BRACK "Faltan corchetes de atributo." #define STRING_TASK_PARSE_ATT_BRACK "Faltan corchetes de atributo."
#define STRING_TASK_PARSE_TAG_BRACK "Faltan corchetes de marca." #define STRING_TASK_PARSE_TAG_BRACK "Faltan corchetes de marca."

View file

@ -396,8 +396,8 @@
#define STRING_CMD_IMPORT_NOFILE "You must specify a file to import." #define STRING_CMD_IMPORT_NOFILE "You must specify a file to import."
#define STRING_CMD_IMPORT_FILE "Importing '{1}'" #define STRING_CMD_IMPORT_FILE "Importing '{1}'"
#define STRING_CMD_IMPORT_NOT_JSON "Not a JSON object: {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_TASK_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_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_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_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." #define STRING_CMD_SHELL_HELP3 "Enter 'quit' (or 'bye', 'exit') to end the session."
@ -779,6 +779,7 @@
// Task // 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_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_ANNO_BRACK "Missing annotation brackets."
#define STRING_TASK_PARSE_ATT_BRACK "Missing attribute brackets." #define STRING_TASK_PARSE_ATT_BRACK "Missing attribute brackets."
#define STRING_TASK_PARSE_TAG_BRACK "Missing tag brackets." #define STRING_TASK_PARSE_TAG_BRACK "Missing tag brackets."

View file

@ -397,8 +397,8 @@
#define STRING_CMD_IMPORT_NOFILE "Specificare il file da importare." #define STRING_CMD_IMPORT_NOFILE "Specificare il file da importare."
#define STRING_CMD_IMPORT_FILE "Importazione di '{1}'" #define STRING_CMD_IMPORT_FILE "Importazione di '{1}'"
#define STRING_CMD_IMPORT_NOT_JSON "Non è un oggetto JSON: {1}" #define STRING_CMD_IMPORT_NOT_JSON "Non è un oggetto JSON: {1}"
#define STRING_CMD_IMPORT_NO_DESC "Annotazione senza descrizione: {1}" #define STRING_TASK_NO_DESC "Annotazione senza descrizione: {1}"
#define STRING_CMD_IMPORT_NO_ENTRY "Annotazione senza data di immissione: {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_HELP1 "Inserisci un comando (es. 'list'), o premi 'Enter'."
#define STRING_CMD_SHELL_HELP2 "Non è necessario includere il comando 'task'." #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." #define STRING_CMD_SHELL_HELP3 "Inserisci 'quit' (o 'bye', 'exit') per terminare la sessione."
@ -780,6 +780,7 @@
// Task // 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_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_ANNO_BRACK "Parentesi di annotazione mancanti."
#define STRING_TASK_PARSE_ATT_BRACK "Parentesi di attributo mancanti." #define STRING_TASK_PARSE_ATT_BRACK "Parentesi di attributo mancanti."
#define STRING_TASK_PARSE_TAG_BRACK "Parentesi di tag mancanti." #define STRING_TASK_PARSE_TAG_BRACK "Parentesi di tag mancanti."

View file

@ -33,7 +33,7 @@ Context context;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv) 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 ("pending"), (int)Task::pending, "textToStatus pending");
test.is ((int)Task::textToStatus ("completed"), (int)Task::completed, "textToStatus completed"); test.is ((int)Task::textToStatus ("completed"), (int)Task::completed, "textToStatus completed");
@ -57,7 +57,7 @@ int main (int argc, char** argv)
after = t3.composeF4 (); after = t3.composeF4 ();
test.is (before, after, "Task::composeF4 -> parse round trip 4 iterations"); test.is (before, after, "Task::composeF4 -> parse round trip 4 iterations");
// Legacy Format 1 // Legacy Format 1 (no longer supported)
// [tags] [attributes] description\n // [tags] [attributes] description\n
// X [tags] [attributes] description\n // X [tags] [attributes] description\n
std::string sample = "[tag1 tag2] [att1:value1 att2:value2] Description"; 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; } try { Task ff1 (sample); } catch (...) { good = false; }
test.notok (good, "Support for ff1 removed"); test.notok (good, "Support for ff1 removed");
// Legacy Format 2 // Legacy Format 2 (no longer supported)
// uuid status [tags] [attributes] description\n // uuid status [tags] [attributes] description\n
sample = "00000000-0000-0000-0000-000000000000 " sample = "00000000-0000-0000-0000-000000000000 "
"- " "- "
"[tag1 tag2] " "[tag1 tag2] "
"[att1:value1 att2:value2] " "[att1:value1 att2:value2] "
"Description"; "Description";
Task ff2 (sample); good = true;
std::string value = ff2.get ("uuid"); try { Task ff2 (sample); } catch (...) { good = false; }
test.is (value, "00000000-0000-0000-0000-000000000000", "ff2 uuid"); test.notok (good, "Support for ff2 removed");
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");
// Legacy Format 3 // Legacy Format 3
// uuid status [tags] [attributes] [annotations] description\n // uuid status [tags] [attributes] [annotations] description\n
@ -99,13 +88,13 @@ int main (int argc, char** argv)
"[att1:value1 att2:value2] " "[att1:value1 att2:value2] "
"[123:ann1 456:ann2] Description"; "[123:ann1 456:ann2] Description";
Task ff3 (sample); Task ff3 (sample);
value = ff2.get ("uuid"); std::string value = ff3.get ("uuid");
test.is (value, "00000000-0000-0000-0000-000000000000", "ff3 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.is (value, "pending", "ff3 status");
test.ok (ff2.hasTag ("tag1"), "ff3 tag1"); test.ok (ff3.hasTag ("tag1"), "ff3 tag1");
test.ok (ff2.hasTag ("tag2"), "ff3 tag2"); test.ok (ff3.hasTag ("tag2"), "ff3 tag2");
test.is (ff2.getTagCount (), 2, "ff3 # tags"); test.is (ff3.getTagCount (), 2, "ff3 # tags");
value = ff3.get ("att1"); value = ff3.get ("att1");
test.is (value, "value1", "ff3 att1"); test.is (value, "value1", "ff3 att1");
value = ff3.get ("att2"); value = ff3.get ("att2");
@ -117,20 +106,20 @@ int main (int argc, char** argv)
// [name:"value" ...]\n // [name:"value" ...]\n
sample = "[" sample = "["
"uuid:\"00000000-0000-0000-0000-000000000000\" " "uuid:\"00000000-0000-0000-0000-000000000000\" "
"status:\"P\" " "status:\"pending\" "
"tags:\"tag1&commaltag2\" " "tags:\"tag1,tag2\" "
"att1:\"value1\" " "att1:\"value1\" "
"att2:\"value2\" " "att2:\"value2\" "
"description:\"Description\"" "description:\"Description\""
"]"; "]";
Task ff4 (sample); Task ff4 (sample);
value = ff2.get ("uuid"); value = ff4.get ("uuid");
test.is (value, "00000000-0000-0000-0000-000000000000", "ff4 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.is (value, "pending", "ff4 status");
test.ok (ff2.hasTag ("tag1"), "ff4 tag1"); test.ok (ff4.hasTag ("tag1"), "ff4 tag1");
test.ok (ff2.hasTag ("tag2"), "ff4 tag2"); test.ok (ff4.hasTag ("tag2"), "ff4 tag2");
test.is (ff2.getTagCount (), 2, "ff4 # tags"); test.is (ff4.getTagCount (), 2, "ff4 # tags");
value = ff4.get ("att1"); value = ff4.get ("att1");
test.is (value, "value1", "ff4 att1"); test.is (value, "value1", "ff4 att1");
value = ff4.get ("att2"); value = ff4.get ("att2");