TempDir: Simplified Temporary Directory for C++


LCOV - code coverage report
Current view: top level - tempdir - tempdir.hpp (source / functions) Hit Total Coverage
Test: filtered_coverage.info Lines: 70 70 100.0 %
Date: 2024-11-29 07:05:09 Functions: 17 17 100.0 %

          Line data    Source code
       1             : // TempDir
       2             : // SPDX-FileCopyrightText: 2024-present Benno Waldhauer
       3             : // SPDX-License-Identifier: MIT
       4             : 
       5             : #pragma once
       6             : 
       7             : #include <chrono>
       8             : #include <filesystem>
       9             : #include <functional>
      10             : #include <iostream>
      11             : #include <random>
      12             : #include <string>
      13             : 
      14             : namespace bw::tempdir
      15             : {
      16             : namespace fs = std::filesystem;
      17             : 
      18           2 : void log(const std::string& message) { std::cout << message << std::endl; }
      19             : 
      20             : // Exception class for errors related to TempDir operations.
      21             : // E.g. for issues encountered during the creation, usage, or deletion of temporary directories.
      22             : // It wraps another exception to retain the original error message.
      23             : class TempDirException : public std::runtime_error
      24             : {
      25             :   public:
      26           4 :     TempDirException(const std::exception& ex)
      27           4 :         : std::runtime_error(std::string("TempDirExcepton: ") + ex.what())
      28             :     {
      29           4 :     }
      30             : };
      31             : 
      32             : //  enum of cleanup modes for TempDir
      33             : //  When a scope with a temporary directory completes, it might be
      34             : //  useful in some cases to be able to view the contents of the temporary directory used by the
      35             : //  scope. Cleanup allows you to control how a TempDir is cleaned up.
      36             : enum class Cleanup
      37             : {
      38             :     always,     // Always clean up a temporary directory after TempDir goes out of scope.
      39             :     on_success, // Clean up a temporary directory after TempDir goes out of scope
      40             :                 // without uncaught exceptions.
      41             :     never       // Never clean up a temporary directory after TempDir goes out of scope.
      42             : };
      43             : 
      44             : // struct holding configuration options for TempDir
      45             : // It allows to specify the root path of temporary directory, the cleanup and logging behavior
      46             : // as well as the temporary directory prefix.
      47             : struct Config
      48             : {
      49             :     fs::path root_path = fs::temp_directory_path();
      50             :     Cleanup cleanup = Cleanup::always;
      51             :     std::string temp_dir_prefix = "temp_dir";
      52             :     std::function<void(const std::string&)> log_impl;
      53             : 
      54           6 :     Config& set_root_path(const fs::path& root_path)
      55             :     {
      56           6 :         this->root_path = root_path;
      57           6 :         return *this;
      58             :     }
      59             : 
      60           7 :     Config& set_cleanup(Cleanup cleanup)
      61             :     {
      62           7 :         this->cleanup = cleanup;
      63           7 :         return *this;
      64             :     }
      65             : 
      66           1 :     Config& set_temp_dir_prefix(const std::string& temp_dir_prefix)
      67             :     {
      68           1 :         this->temp_dir_prefix = temp_dir_prefix;
      69           1 :         return *this;
      70             :     }
      71             : 
      72           1 :     Config& enable_logging()
      73             :     {
      74           1 :         this->log_impl = bw::tempdir::log;
      75           1 :         return *this;
      76             :     }
      77             : 
      78           1 :     Config& enable_logging(std::function<void(const std::string&)> log_impl)
      79             :     {
      80           1 :         this->log_impl = log_impl;
      81           1 :         return *this;
      82             :     }
      83             : };
      84             : 
      85             : // TempDir manages temporary directories with automatic cleanup based on user-defined policies.
      86             : //
      87             : // The TempDir class is designed to simplify the creation and management of temporary directories.
      88             : // It supports automatic cleanup based on configurable policies (e.g., always cleanup, cleanup only
      89             : // on successful execution, or never cleanup). The class ensures proper handling of errors during
      90             : // directory creation and cleanup, and on demand logs relevant messages for each operation.
      91             : //
      92             : // The directory is created in a user-specified or default root path and given a unique name
      93             : // generated based on a timestamp and a random number. Errors related to directory operations
      94             : // are wrapped in TempDirException.
      95             : //
      96             : // Cleanup will be automatically handled based on the configured policy when the TempDir object goes
      97             : // out of scope or when cleanup method is called explicitly
      98             : class TempDir
      99             : {
     100             :   public:
     101             :     // Constructs a TempDir with a specified root path
     102             :     // where the temporary directory will be created.
     103           5 :     explicit TempDir(fs::path root_path) : TempDir(Config().set_root_path(root_path)) {}
     104             : 
     105             :     //  Constructs a TempDir with a specified root path and cleanup policy.
     106             :     //  root_path: The root path where the temporary directory will be created.
     107             :     //  cleanup: The cleanup policy to apply when the object is destroyed.
     108           1 :     explicit TempDir(fs::path root_path, Cleanup cleanup)
     109           1 :         : TempDir(Config().set_root_path(root_path).set_cleanup(cleanup))
     110             :     {
     111           1 :     }
     112             : 
     113             :     // Constructs a TempDir with a specified cleanup policy
     114             :     // which will be applied when the object is destroyed.
     115           6 :     explicit TempDir(Cleanup cleanup) : TempDir(Config().set_cleanup(cleanup)) {}
     116             : 
     117             :     // Constructs a TempDir with a fully specified configuration for the TempDir,
     118             :     // including path, prefix, cleanup policy, and logging.
     119             :     // If an error occurs during construction, a TempDirException is thrown.
     120          17 :     explicit TempDir(Config config = {}) : _config(config)
     121             :     {
     122             :         try
     123             :         {
     124          17 :             _temp_dir = config.root_path / generate_dir_name();
     125          17 :             fs::create_directories(_temp_dir);
     126          16 :             log("TempDir create '" + _temp_dir.string() + "'");
     127             :         }
     128           1 :         catch (const std::exception& ex)
     129             :         {
     130           1 :             log("TempDir creation of '" + _temp_dir.string() + "' failed. Error: " + ex.what());
     131           1 :             throw TempDirException(ex);
     132           1 :         }
     133          18 :     }
     134             : 
     135             :     // Destructor that handles automatic cleanup based on the configured policy.
     136             :     //
     137             :     // Attempts to clean up the temporary directory if the cleanup policy allows it.
     138             :     // Errors during cleanup are logged but not rethrown
     139          17 :     ~TempDir()
     140             :     {
     141             :         try
     142             :         {
     143          17 :             cleanup();
     144             :         }
     145           2 :         catch (const std::exception& e)
     146             :         {
     147             :             // do nothing as rethrowing is not allowed in destructor
     148             :             // error was already logged in cleanup method
     149           2 :         }
     150          17 :     }
     151             : 
     152             :     // Copying TempDir is disabled
     153             :     TempDir(const TempDir&) = delete;
     154             :     TempDir& operator=(const TempDir&) = delete;
     155             : 
     156             :     // Moving TempDir is enabeld
     157           1 :     TempDir(TempDir&&) = default;
     158             :     TempDir& operator=(TempDir&&) = default;
     159             : 
     160             :     // Returns the path of the managed temporary directory.
     161          22 :     const std::filesystem::path& path() const { return _temp_dir; }
     162             : 
     163             :     // Manually triggers cleanup of the temporary directory.
     164             :     // Attempts to delete the directory and its contents based on the configured
     165             :     // cleanup policy. If an error occurs during cleanup, a TempDirException is thrown.
     166          18 :     void cleanup()
     167             :     {
     168          18 :         if (!fs::exists(_temp_dir))
     169           4 :             return;
     170             : 
     171          14 :         bool always_cleanup = _config.cleanup == Cleanup::always;
     172          14 :         bool on_sucess_cleanup_no_exception =
     173          14 :             _config.cleanup == Cleanup::on_success && std::uncaught_exceptions() <= 0;
     174             : 
     175          14 :         if (!always_cleanup && !on_sucess_cleanup_no_exception)
     176             :         {
     177           3 :             log("TempDir keep '" + _temp_dir.string() + "'");
     178           3 :             return;
     179             :         }
     180             : 
     181             :         try
     182             :         {
     183          11 :             fs::remove_all(_temp_dir);
     184           8 :             log("TempDir remove '" + _temp_dir.string() + "'");
     185             :         }
     186           3 :         catch (const std::exception& ex)
     187             :         {
     188           3 :             log("TempDir removal of '" + _temp_dir.string() + "' failed. Error: " + ex.what());
     189           3 :             throw TempDirException(ex);
     190           3 :         }
     191             :     }
     192             : 
     193             :   private:
     194             :     // Generates a unique name for the temporary directory.
     195          17 :     std::string generate_dir_name()
     196             :     {
     197             :         using namespace std::chrono;
     198             : 
     199          17 :         std::random_device rd;
     200          17 :         std::mt19937 gen(rd());
     201          17 :         std::uniform_int_distribution<> dist(10000, 99999);
     202          17 :         int random_number = dist(gen);
     203             : 
     204          17 :         auto now = system_clock::now();
     205          17 :         auto timestamp = duration_cast<milliseconds>(now.time_since_epoch()).count();
     206             : 
     207          17 :         std::string ts = std::to_string(timestamp);
     208          17 :         std::string rn = std::to_string(random_number);
     209          51 :         return _config.temp_dir_prefix + "_" + ts + "_" + rn;
     210          17 :     }
     211             : 
     212             :     // Logs a message using the configured logging implementation.
     213          31 :     void log(const std::string& message)
     214             :     {
     215          31 :         if (_config.log_impl)
     216           4 :             _config.log_impl(message);
     217          31 :     }
     218             : 
     219             :     std::filesystem::path _temp_dir;
     220             :     Config _config;
     221             : };
     222             : 
     223             : } // namespace bw::tempdir

Generated by: LCOV version 1.14