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 '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
View file

@ -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.
---

View file

@ -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}"

View file

@ -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,10 +853,11 @@ std::string Task::composeJSON (bool include_id /*= false*/) const
}
// Include urgency.
out << ","
<< "\"urgency\":\""
<< urgency_c ()
<<"\"";
if (decorate)
out << ","
<< "\"urgency\":\""
<< urgency_c ()
<<"\"";
out << "}";
return out.str ();
@ -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
////////////////////////////////////////////////////////////////////////////////

View file

@ -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

View file

@ -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 ();

View file

@ -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."

View file

@ -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."

View file

@ -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."

View file

@ -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."

View file

@ -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");