Check serialization before writing to database

Ensure that the IntervalFactory can parse the string returned by
Interval::serialize before writing to database.

If a user attempts to add a non-parseable date, you will get an error like:

  $ TZ="GMT" timew track 7h before 1970-01-01T00:00:00 test
  Note: 'test' is a new tag.
  Internal error. Failed encode / decode check.

Or if the user attempts to add a tag that is not supported:

  $ timew start 1970-01-01T00:00:00 '\"TEST\"'
  Note: '"\\"TEST\\""' is a new tag.
  Internal error. Failed encode / decode check.

On #159, the serializer / deserializer should be able to handle the escaped
quotes, but it is another example where we do not want any entry written into
the database that cannot be properly parsed. This change will also add more
verbose debugging information if the :debug flag is passed, like:

  $ timew start 1970-01-01T00:00:00 '\"TEST\"' :debug
  CLI Parser
    _original_args
      timew start 1970-01-01T00:00:00 \"TEST\" :debug
    _args
      word basename='timew' raw='timew' BINARY
      word canonical='start' raw='start' ORIGINAL CMD
      date raw='1970-01-01T00:00:00' ORIGINAL FILTER
      word raw='\"TEST\"' ORIGINAL FILTER TAG
      word canonical=':debug' raw=':debug' ORIGINAL HINT FILTER

  >> 2020-02.data: 0 intervals
  >> 1970-01.data: 0 intervals
  >> 1969-12.data: 0 intervals
  >> Loaded 0 tracked intervals
  Note: '"\\"TEST\\""' is a new tag.
  >> Datafile::addInterval() failed.
  >> Encode / decode check failed:
  >>   inc 19700101T060000Z # "\\"TEST\\""
  >> is not equal to:
  >>   inc 19700101T060000Z # "TEST\\\"\"" \
  Internal error. Failed encode / decode check.
  >> Timer timew 0.002137 sec

Closes #290
Related to #159

Signed-off-by: Shaun Ruffell <sruffell@sruffell.net>
This commit is contained in:
Shaun Ruffell 2020-03-03 20:32:41 -06:00 committed by lauft
parent fda978c8cc
commit c1f3d8be72

View file

@ -33,6 +33,7 @@
#include <cassert>
#include <stdlib.h>
#include <AtomicFile.h>
#include <IntervalFactory.h>
////////////////////////////////////////////////////////////////////////////////
void Datafile::initialize (const std::string& name)
@ -96,13 +97,29 @@ void Datafile::addInterval (const Interval& interval)
if (! _lines_loaded)
load_lines ();
// TODO if interval is not a duplicate
// TODO insert interval.serialize into _lines
// TODO _dirty = true;
const std::string serialization = interval.serialize ();
_lines.push_back (interval.serialize ());
debug (format ("{1}: Added {2}", _file.name (), _lines.back ()));
_dirty = true;
// Ensure that the IntervalFactory can properly parse the serialization before
// adding it to the database.
try
{
Interval test = IntervalFactory::fromSerialization (serialization);
test.id = interval.id;
if (interval != test)
{
throw (format ("Encode / decode check failed:\n {1}\nis not equal to:\n {2}",
serialization, test.serialize ()));
}
_lines.push_back (serialization);
debug (format ("{1}: Added {2}", _file.name (), _lines.back ()));
_dirty = true;
}
catch (const std::string& error)
{
debug (format ("Datafile::addInterval() failed.\n{1}", error));
throw std::string ("Internal error. Failed encode / decode check.");
}
}
////////////////////////////////////////////////////////////////////////////////