diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 34bbb3e50..a1df16eb4 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -1274,18 +1274,101 @@ void TDB2::merge (const std::string& mergeFile) //////////////////////////////////////////////////////////////////////////////// void TDB2::revert () { + // Extract the details of the last txn, and roll it back. std::vector u = undo.get_lines (); + std::string uuid; + std::string when; + std::string current; + std::string prior; + revert_undo (u, uuid, when, current, prior); + + // Display diff and confirm. + show_diff (current, prior, when); + if (! context.config.getBoolean ("confirmation") || + confirm (STRING_TDB2_UNDO_CONFIRM)) + { + // There are six kinds of change possible. Determine which one, and act + // accordingly. + // + // Revert: task add + // [1] 0 --> p + // - erase from pending + // - if in backlog, erase, else add deletion + // + // Revert: task modify + // [2] p --> p' + // - write prior over current in pending + // - add prior to backlog + // + // Revert: task done/delete + // [3] p --> c + // - add prior to pending + // - erase from completed + // - add prior to backlog + // + // Revert: task modify + // [4] c --> p + // - add prior to completed + // - erase from pending + // - add prior to backlog + // + // Revert: task modify + // [5] c --> c' + // - write prior over current in completed + // - add prior to backlog + // + // Revert: task log + // [6] 0 --> c + // - erase from completed + // - if in backlog, erase, else add deletion + + // Modify other data files accordingly. + std::vector p = pending.get_lines (); + revert_pending (p, uuid, current, prior); + + std::vector c = completed.get_lines (); + revert_completed (p, c, uuid, current, prior); + + std::vector b = backlog.get_lines (); + revert_backlog (b, uuid, current, prior); + + // Commit. If processing makes it this far with no exceptions, then we're + // done. + File::write (undo._file._data, u); + File::write (pending._file._data, p); + File::write (completed._file._data, c); + File::write (backlog._file._data, b); + +/* + // Perhaps user hand-edited the data files? + // Perhaps the task was in completed.data, which was still in file format 3? + std::cout << format (STRING_TDB2_MISSING_TASK, uuid.substr (6, 36)) + << "\n" + << STRING_TDB2_UNDO_IMPOSSIBLE + << "\n"; +*/ + } + else + std::cout << STRING_CMD_CONFIG_NO_CHANGE << "\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB2::revert_undo ( + std::vector & u, + std::string& uuid, + std::string& when, + std::string& current, + std::string& prior) +{ if (u.size () < 3) throw std::string (STRING_TDB2_NO_UNDO); // pop last tx u.pop_back (); // separator. - std::string current = u.back ().substr (4); + current = u.back ().substr (4); u.pop_back (); - std::string prior; - std::string when; if (u.back ().substr (0, 5) == "time ") { when = u.back ().substr (5); @@ -1300,6 +1383,125 @@ void TDB2::revert () u.pop_back (); } + // Extract identifying uuid. + std::string::size_type uuidAtt = current.find ("uuid:\""); + if (uuidAtt != std::string::npos) + uuid = current.substr (uuidAtt, 43); // 43 = uuid:"..." + else + throw std::string (STRING_TDB2_MISSING_UUID); +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB2::revert_pending ( + std::vector & p, + const std::string& uuid, + const std::string& current, + const std::string& prior) +{ + // is 'current' in pending? + std::vector ::iterator task; + for (task = p.begin (); task != p.end (); ++task) + { + if (task->find (uuid) != std::string::npos) + { + context.debug ("TDB::revert - task found in pending.data"); + + // Either revert if there was a prior state, or remove the task. + if (prior != "") + { + *task = prior; + std::cout << STRING_TDB2_REVERTED << "\n"; + } + else + { + p.erase (task); + std::cout << STRING_TDB2_REMOVED << "\n"; + } + + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB2::revert_completed ( + std::vector & p, + std::vector & c, + const std::string& uuid, + const std::string& current, + const std::string& prior) +{ + // is 'current' in completed? + std::vector ::iterator task; + for (task = c.begin (); task != c.end (); ++task) + { + if (task->find (uuid) != std::string::npos) + { + context.debug ("TDB::revert - task found in completed.data"); + + // Either revert if there was a prior state, or remove the task. + if (prior != "") + { + *task = prior; + if (task->find ("status:\"pending\"") != std::string::npos || + task->find ("status:\"waiting\"") != std::string::npos || + task->find ("status:\"recurring\"") != std::string::npos) + { + c.erase (task); + p.push_back (prior); + std::cout << STRING_TDB2_REVERTED << "\n"; + context.debug ("TDB::revert - task belongs in pending.data"); + } + else + { + std::cout << STRING_TDB2_REVERTED << "\n"; + context.debug ("TDB::revert - task belongs in completed.data"); + } + } + else + { + c.erase (task); + + std::cout << STRING_TDB2_REVERTED << "\n"; + context.debug ("TDB::revert - task removed"); + } + + std::cout << STRING_TDB2_UNDO_COMPLETE << "\n"; + break; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB2::revert_backlog ( + std::vector & b, + const std::string& uuid, + const std::string& current, + const std::string& prior) +{ + std::vector ::iterator task; + for (task = b.begin (); task != b.end (); ++task) + { + if (task->find (uuid) != std::string::npos) + { +/* + context.debug ("TDB::revert_backlog - task found in backlog.data"); + +// // Write prior to backlog.data +// Task json_task (prior); +// backlog.add_line (json_task.composeJSON () + "\n"); + +*/ + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void TDB2::show_diff ( + const std::string& current, + const std::string& prior, + const std::string& when) +{ Date lastChange (strtol (when.c_str (), NULL, 10)); // Set the colors. @@ -1512,106 +1714,6 @@ void TDB2::revert () << "\n"; } - // Output displayed, now confirm. - if (context.config.getBoolean ("confirmation") && - !confirm (STRING_TDB2_UNDO_CONFIRM)) - { - std::cout << STRING_CMD_CONFIG_NO_CHANGE << "\n"; - return; - } - - // Extract identifying uuid. - std::string uuid; - std::string::size_type uuidAtt = current.find ("uuid:\""); - if (uuidAtt != std::string::npos) - uuid = current.substr (uuidAtt, 43); // 43 = uuid:"..." - else - throw std::string (STRING_TDB2_MISSING_UUID); - - // load pending.data - std::vector p = pending.get_lines (); - - // is 'current' in pending? - std::vector ::iterator task; - for (task = p.begin (); task != p.end (); ++task) - { - if (task->find (uuid) != std::string::npos) - { - context.debug ("TDB::undo - task found in pending.data"); - - // Either revert if there was a prior state, or remove the task. - if (prior != "") - { - *task = prior; - std::cout << STRING_TDB2_REVERTED << "\n"; - } - else - { - p.erase (task); - std::cout << STRING_TDB2_REMOVED << "\n"; - } - - // Rewrite files. - File::write (pending._file._data, p); - File::write (undo._file._data, u); - return; - } - } - - // load completed.data - std::vector c = completed.get_lines (); - - // is 'current' in completed? - for (task = c.begin (); task != c.end (); ++task) - { - if (task->find (uuid) != std::string::npos) - { - context.debug ("TDB::undo - task found in completed.data"); - - // Either revert if there was a prior state, or remove the task. - if (prior != "") - { - *task = prior; - if (task->find ("status:\"pending\"") != std::string::npos || - task->find ("status:\"waiting\"") != std::string::npos || - task->find ("status:\"recurring\"") != std::string::npos) - { - c.erase (task); - p.push_back (prior); - File::write (completed._file._data, c); - File::write (pending._file._data, p); - File::write (undo._file._data, u); - std::cout << STRING_TDB2_REVERTED << "\n"; - context.debug ("TDB::undo - task belongs in pending.data"); - } - else - { - File::write (completed._file._data, c); - File::write (undo._file._data, u); - std::cout << STRING_TDB2_REVERTED << "\n"; - context.debug ("TDB::undo - task belongs in completed.data"); - } - } - else - { - c.erase (task); - File::write (completed._file._data, c); - File::write (undo._file._data, u); - std::cout << STRING_TDB2_REVERTED << "\n"; - context.debug ("TDB::undo - task removed"); - } - - std::cout << STRING_TDB2_UNDO_COMPLETE << "\n"; - return; - } - } - - // Perhaps user hand-edited the data files? - // Perhaps the task was in completed.data, which was still in file format 3? - std::cout << format (STRING_TDB2_MISSING_TASK, uuid.substr (6, 36)) - << "\n" - << STRING_TDB2_UNDO_IMPOSSIBLE - << "\n"; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/TDB2.h b/src/TDB2.h index 199724828..292417fd0 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -127,6 +127,11 @@ public: private: bool verifyUniqueUUID (const std::string&); + void show_diff (const std::string&, const std::string&, const std::string&); + void revert_undo (std::vector &, std::string&, std::string&, std::string&, std::string&); + void revert_pending (std::vector &, const std::string&, const std::string&, const std::string&); + void revert_completed (std::vector &, std::vector &, const std::string&, const std::string&, const std::string&); + void revert_backlog (std::vector &, const std::string&, const std::string&, const std::string&); public: TF2 pending;