TempDir: Simplified Temporary Directory for C++


LCOV - code coverage report
Current view: top level - tempdir - tempdir.hpp (source / functions) Coverage Total Hit
Test: filtered_coverage.info Lines: 100.0 % 70 70
Test Date: 2025-02-21 07:38:01 Functions: 100.0 % 17 17

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

Generated by: LCOV version 2.0-1