From f1639e686220789266fcc80c5ec5f87cae504cbc Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 18 Oct 2014 23:47:32 -0400 Subject: [PATCH] Hooks - Revised all event functions to use the new callHookScript method, which is now common to all events. --- src/Hooks.cpp | 349 +++++++++++++++++++++++++------------------------- src/Hooks.h | 1 + 2 files changed, 179 insertions(+), 171 deletions(-) diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 1863057b7..b0608ced6 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -100,10 +100,8 @@ bool Hooks::enable (bool value) // Output: // - all emitted JSON lines are added/modified as tasks, if the exit code is // zero, otherwise ignored. -// - minimal new task: {"description":"Buy milk"} -// - to modify a task include complete JSON -// - all emitted non-JSON lines are considered feedback messages if the exit -// code is zero, otherwise they are considered errors. +// - all emitted non-JSON lines are considered feedback or error messages +// depending on the status code. // void Hooks::onLaunch () { @@ -113,50 +111,38 @@ void Hooks::onLaunch () context.timer_hooks.start (); std::vector matchingScripts = scripts ("on-launch"); - std::vector ::iterator i; - for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i) + if (matchingScripts.size ()) { - if (_debug >= 1) - context.debug ("Hooks: Calling " + *i); - - std::string output; - std::vector args; - int status = execute (*i, args, "", output); - - if (_debug >= 2) - context.debug (format ("Hooks: Completed with status {1}", status)); - - std::vector lines; - split (lines, output, '\n'); - std::vector ::iterator line; - - if (status == 0) + std::vector ::iterator script; + for (script = matchingScripts.begin (); script != matchingScripts.end (); ++script) { - for (line = lines.begin (); line != lines.end (); ++line) + std::vector input; + std::vector output; + int status = callHookScript (*script, input, output); + + std::vector ::iterator line; + for (line = output.begin (); line != output.end (); ++line) { if (isJSON (*line)) { - if (_debug >= 2) + if (status == 0) { - context.debug ("Hook output:"); - context.debug (" " + *line); + // Only 'add' is possible. + Task newTask (*line); + context.tdb2.add (newTask); } - - // Only 'add' is possible. - Task newTask (*line); - context.tdb2.add (newTask); } else - context.header (*line); + { + if (status == 0) + context.header (*line); + else + context.error (*line); + } } - } - else - { - for (line = lines.begin (); line != lines.end (); ++line) - if (! isJSON (*line)) - context.error (*line); - throw 0; // This is how hooks silently terminate processing. + if (status) + throw 0; // This is how hooks silently terminate processing. } } @@ -172,8 +158,8 @@ void Hooks::onLaunch () // // Output: // - any emitted JSON is ignored -// - all emitted non-JSON lines are considered feedback messages if the exit -// code is zero, otherwise they are considered errors. +// - all emitted non-JSON lines are considered feedback or error messages +// depending on the status code. // void Hooks::onExit () { @@ -182,52 +168,40 @@ void Hooks::onExit () context.timer_hooks.start (); - std::vector changes; - context.tdb2.get_changes (changes); - - std::string input = ""; - std::vector ::const_iterator t; - for (t = changes.begin (); t != changes.end (); ++t) - { - std::string json = t->composeJSON (); - if (_debug >= 2) - context.debug ("Hook input: " + json); - - input += json + "\n"; - } - std::vector matchingScripts = scripts ("on-exit"); - std::vector ::iterator i; - for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i) + if (matchingScripts.size ()) { - if (_debug >= 1) - context.debug ("Hooks: Calling " + *i); + // Get the set of changed tasks. + std::vector tasks; + context.tdb2.get_changes (tasks); - std::string output; - std::vector args; - int status = execute (*i, args, input, output); + // Convert to a vector of strings. + std::vector input; + std::vector ::const_iterator t; + for (t = tasks.begin (); t != tasks.end (); ++t) + input.push_back (t->composeJSON ()); - if (_debug >= 2) - context.debug (format ("Hooks: Completed with status {1}", status)); - - std::vector lines; - split (lines, output, '\n'); - std::vector ::iterator line; - - if (_debug >= 2) - context.debug ("Hook output:"); - - for (line = lines.begin (); line != lines.end (); ++line) + // Call the hook scripts, with the invariant input. + std::vector ::iterator script; + for (script = matchingScripts.begin (); script != matchingScripts.end (); ++script) { - if (! isJSON (*line)) - { - if (_debug >= 2) - context.debug (" " + *line); + std::vector output; + int status = callHookScript (*script, input, output); - if (status == 0) - context.footnote (*line); + std::vector ::iterator line; + for (line = output.begin (); line != output.end (); ++line) + { + if (isJSON (*line)) + { + context.error ("JSON output ignored: {1}"); + } else - context.error (*line); + { + if (status == 0) + context.footnote (*line); + else + context.error (*line); + } } } } @@ -244,12 +218,10 @@ void Hooks::onExit () // Output: // - all emitted JSON lines are added/modified as tasks, if the exit code is // zero, otherwise ignored. -// - minimal new task: {"description":"Buy milk"} -// - to modify a task include complete JSON -// - all emitted non-JSON lines are considered feedback messages if the exit -// code is zero, otherwise they are considered errors. +// - all emitted non-JSON lines are considered feedback or error messages +// depending on the status code. // -void Hooks::onAdd (std::vector & changes) +void Hooks::onAdd (std::vector & tasks) { if (! _enabled) return; @@ -257,54 +229,49 @@ void Hooks::onAdd (std::vector & changes) context.timer_hooks.start (); std::vector matchingScripts = scripts ("on-add"); - std::vector ::iterator i; - for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i) + if (matchingScripts.size ()) { - if (_debug >= 1) - context.debug ("Hooks: Calling " + *i); + // Convert vector of tasks to a vector of strings. + std::vector input; + std::vector ::const_iterator t; + for (t = tasks.begin (); t != tasks.end (); ++t) + input.push_back (t->composeJSON ()); - std::string input = changes.at(0).composeJSON (); - if (_debug >= 2) - context.debug ("Hook input: " + input); - - input += "\n"; - std::string output; - std::vector args; - int status = execute (*i, args, input, output); - - if (_debug >= 2) - context.debug (format ("Hooks: Completed with status {1}", status)); - - std::vector lines; - split (lines, output, '\n'); - std::vector ::iterator line; - - if (status == 0) + // Call the hook scripts. + std::vector ::iterator script; + for (script = matchingScripts.begin (); script != matchingScripts.end (); ++script) { - changes.clear (); + std::vector output; + int status = callHookScript (*script, input, output); - if (_debug >= 2) - context.debug ("Hook output:"); + input.clear (); - for (line = lines.begin (); line != lines.end (); ++line) + std::vector ::iterator line; + for (line = output.begin (); line != output.end (); ++line) { - if (_debug >= 2) - context.debug (" " + *line); - if (isJSON (*line)) - changes.push_back (Task (*line)); + { + if (status == 0) + input.push_back (*line); + } else - context.footnote (*line); + { + if (status == 0) + context.footnote (*line); + else + context.error (*line); + } } - } - else - { - for (line = lines.begin (); line != lines.end (); ++line) - if (! isJSON (*line)) - context.error (*line); - throw 0; // This is how hooks silently terminate processing. + if (status) + throw 0; // This is how hooks silently terminate processing. } + + // Transfer the modified task lines back to the original task list. + tasks.clear (); + std::vector ::iterator i; + for (i = input.begin (); i != input.end (); ++i) + tasks.push_back (Task (*i)); } context.timer_hooks.stop (); @@ -320,75 +287,74 @@ void Hooks::onAdd (std::vector & changes) // Output: // - all emitted JSON lines are added/modified as tasks, if the exit code is // zero, otherwise ignored. -// - minimal new task: {"description":"Buy milk"} -// - to modify a task include complete JSON -// - all emitted non-JSON lines are considered feedback messages if the exit -// code is zero, otherwise they are considered errors. +// - all emitted non-JSON lines are considered feedback or error messages +// depending on the status code. // -void Hooks::onModify (const Task& before, std::vector & changes) +void Hooks::onModify (const Task& before, std::vector & tasks) { - if (! _enabled) + if (! _enabled || tasks.size () < 1) return; context.timer_hooks.start (); - std::vector matchingScripts = scripts ("on-modify"); - std::vector ::iterator i; - for (i = matchingScripts.begin (); i != matchingScripts.end (); ++i) + std::vector matchingScripts = scripts ("on-modіfy"); + if (matchingScripts.size ()) { - if (_debug >= 1) - context.debug ("Hooks: Calling " + *i); - + // Prepare invariants. std::string beforeJSON = before.composeJSON (); - std::string afterJSON = changes[0].composeJSON (); - if (_debug >= 2) + std::string uuidPattern = "\"uuid\":\"" + before.get ("uuid") + "\""; + + // Convert vector of tasks to a vector of strings. + std::vector input; + input.push_back (beforeJSON); // [0] original, never changes + input.push_back (tasks[0].composeJSON ()); // [1] original' + + // Call the hook scripts. + std::vector ::iterator script; + for (script = matchingScripts.begin (); script != matchingScripts.end (); ++script) { - context.debug ("Hook input:"); - context.debug (" " + beforeJSON); - context.debug (" " + afterJSON); - } + std::vector firstTwoOnly; + firstTwoOnly.push_back (input[0]); + firstTwoOnly.push_back (input[1]); - std::string input = beforeJSON - + "\n" - + afterJSON - + "\n"; - std::string output; - std::vector args; - int status = execute (*i, args, input, output); + std::vector output; + int status = callHookScript (*script, firstTwoOnly, output); - if (_debug >= 2) - context.debug (format ("Hooks: Completed with status {1}", status)); + // Start from scratch. + input[1] = ""; // [1] placeholder for original' - std::vector lines; - split (lines, output, '\n'); - std::vector ::iterator line; - - if (status == 0) - { - changes.clear (); - - if (_debug >= 2) - context.debug ("Hook output:"); - - for (line = lines.begin (); line != lines.end (); ++line) + std::vector ::iterator line; + for (line = output.begin (); line != output.end (); ++line) { - if (_debug >= 2) - context.debug (" " + *line); - if (isJSON (*line)) - changes.push_back (Task (*line)); + { + if (status == 0) + { + if (line->find (uuidPattern) != std::string::npos) + input[1] = *line; // [1] original' + else + input.push_back (*line); // [n > 1] extras + } + } else - context.footnote (*line); + { + if (status == 0) + context.footnote (*line); + else + context.error (*line); + } } - } - else - { - for (line = lines.begin (); line != lines.end (); ++line) - if (! isJSON (*line)) - context.error (*line); - throw 0; // This is how hooks silently terminate processing. + if (status) + throw 0; // This is how hooks silently terminate processing. } + + // Transfer the modified task lines back to the original task list. + tasks.clear (); + std::vector ::iterator i; + for (i = input.begin (); i != input.end (); ++i) + if (i != input.begin ()) + tasks.push_back (Task (*i)); } context.timer_hooks.stop (); @@ -480,3 +446,44 @@ bool Hooks::isJSON (const std::string& input) const } //////////////////////////////////////////////////////////////////////////////// +int Hooks::callHookScript ( + const std::string& script, + const std::vector & input, + std::vector output) +{ + if (_debug >= 1) + context.debug ("Hooks: Calling " + script); + + if (_debug >= 2) + { + context.debug ("Hook input:"); + std::vector ::const_iterator i; + for (i = input.begin (); i != input.end (); ++i) + context.debug (" " + *i); + } + + std::string inputStr; + join (inputStr, "\n", input); + + std::string outputStr; + std::vector args; + int status = execute (script, args, inputStr, outputStr); + + split (output, outputStr, '\n'); + + if (_debug >= 2) + { + context.debug ("Hook output:"); + std::vector ::const_iterator i; + for (i = output.begin (); i != output.end (); ++i) + if (*i != "") + context.debug (" " + *i); + + context.debug (format ("Hooks: Completed with status {1}", status)); + context.debug (" "); // Blank line + } + + return status; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Hooks.h b/src/Hooks.h index e0b713c3c..097bfc7f7 100644 --- a/src/Hooks.h +++ b/src/Hooks.h @@ -52,6 +52,7 @@ public: private: std::vector scripts (const std::string&); bool isJSON (const std::string&) const; + int callHookScript (const std::string&, const std::vector &, std::vector ); private: bool _enabled;