aboutsummaryrefslogtreecommitdiff
path: root/src/tracking
diff options
context:
space:
mode:
Diffstat (limited to 'src/tracking')
-rw-r--r--src/tracking/change_tracker.cc92
-rw-r--r--src/tracking/change_tracker.h32
2 files changed, 124 insertions, 0 deletions
diff --git a/src/tracking/change_tracker.cc b/src/tracking/change_tracker.cc
new file mode 100644
index 0000000..913604e
--- /dev/null
+++ b/src/tracking/change_tracker.cc
@@ -0,0 +1,92 @@
+#include "change_tracker.h"
+
+#include <boost/optional.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+namespace {
+
+// constants for increasing pair access readability
+constexpr unsigned int EMPLACE_SUCCESS = 1;
+constexpr unsigned int FILE_NAME = 0;
+constexpr unsigned int DIFF_PROCESS = 1;
+
+boost::process::context getDefaultContext() {
+ boost::process::context context;
+
+ context.environment = boost::process::self::get_environment();
+ context.stdout_behavior = boost::process::capture_stream();
+ context.stdin_behavior = boost::process::capture_stream();
+
+ return context;
+}
+
+}
+
+namespace tracking {
+
+ChangeTracker::ChangeTracker(utility::Logger* logger):
+ logger_(logger),
+ children_() { }
+
+ChangeTracker::~ChangeTracker() {
+ for ( auto&& tracked : this->children_ ) {
+ std::get<DIFF_PROCESS>(tracked)->get_stdin().close();
+
+ this->logger_->forward(std::get<DIFF_PROCESS>(tracked)->get_stdout());
+
+ tracked.second->wait();
+ }
+}
+
+bool ChangeTracker::is_tracked(const std::string& file_path) const {
+ return this->children_.find(file_path) != this->children_.end();
+}
+
+// Begins tracking changes to a file reachable by a given path
+//
+// The actual tracking is performed by a `diff` instance that is
+// spawned by this method and managed by this class.
+// As `diff` is called as a new subprocess and because there is no
+// straight forward way for checking / enforcing if it has already
+// completed reading the initial contents of the file the command
+// is structured in the following sequence:
+//
+// diff -p - $file_path
+//
+// This means that reading the final file contents is delegated to
+// `diff` while the initial file contents are read by this method
+// and written to `diff`'s standard input.
+//
+// If the `-` and `$file_path` arguments were exchanged there would
+// be no way to:
+// - be sure the initial contents are read before the changing
+// syscall that triggered this tracking in the first place is
+// performed
+// - be sure that the initial file contents are read completly
+// as `diff` seemingly only reads the first block of the first
+// file provided if the second file argument is standard input
+//
+bool ChangeTracker::track(const std::string& file_path) {
+ auto result = this->children_.emplace(
+ file_path,
+ std::make_unique<boost::process::child>(
+ boost::process::launch_shell("diff -p - " + file_path, getDefaultContext())
+ )
+ );
+
+ if ( std::get<EMPLACE_SUCCESS>(result) ) {
+ boost::filesystem::ifstream file(
+ std::get<FILE_NAME>(*result.first)
+ );
+
+ if ( file.is_open() ) {
+ std::get<DIFF_PROCESS>(*result.first)->get_stdin() << file.rdbuf();
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+}
diff --git a/src/tracking/change_tracker.h b/src/tracking/change_tracker.h
new file mode 100644
index 0000000..54af4f7
--- /dev/null
+++ b/src/tracking/change_tracker.h
@@ -0,0 +1,32 @@
+#ifndef CHANGE_SRC_TRACKING_CHANGE_TRACKER_H_
+#define CHANGE_SRC_TRACKING_CHANGE_TRACKER_H_
+
+#include <unordered_map>
+
+#include <boost/process.hpp>
+
+#include "utility/logger.h"
+
+namespace tracking {
+
+class ChangeTracker {
+ public:
+ ChangeTracker(utility::Logger*);
+ ~ChangeTracker();
+
+ bool is_tracked(const std::string&) const;
+
+ bool track(const std::string&);
+
+ private:
+ utility::Logger* const logger_;
+
+ std::unordered_map<
+ std::string, std::unique_ptr<boost::process::child>
+ > children_;
+
+};
+
+}
+
+#endif // CHANGE_SRC_TRACKING_CHANGE_TRACKER_H_