File: TemporaryOutputStream.h

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (110 lines) | stat: -rw-r--r-- 2,874 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#pragma once

#include "i18n.h"
#include <string>
#include <fstream>
#include "os/fs.h"
#include "fmt/format.h"

namespace stream
{

// An output stream wrapper which opens a temporary file next to the actual target,
// moving the temporary file over the target file on demand.
// If something goes wrong, a std::runtime_error is thrown.
class TemporaryOutputStream
{
private:
    fs::path _targetFile;
    fs::path _temporaryPath;

    std::ofstream _stream;

public:
    TemporaryOutputStream(const fs::path& targetFile) :
        _targetFile(targetFile),
        _temporaryPath(getTemporaryPath(_targetFile)),
        _stream(_temporaryPath)
    {
        if (!_stream.is_open())
        {
            throw std::runtime_error(fmt::format(_("Cannot open file for writing: {0}"), _temporaryPath.string()));
        }
    }

    ~TemporaryOutputStream()
    {
        // Remove the temporary file if it still existing at the End Of Life of this object
        if (_stream.is_open())
        {
            _stream.close();
        }

        if (fs::exists(_temporaryPath))
        {
            rMessage() << "Cleaning up temporary file " << _temporaryPath << std::endl;

            try
            {
                fs::remove(_temporaryPath);
            }
            catch (fs::filesystem_error& e)
            {
                rError() << "Could not remove the temporary file " << _temporaryPath << std::endl
                    << e.what() << std::endl;
            }
        }
    }

    std::ostream& getStream()
    {
        return _stream;
    }

    void closeAndReplaceTargetFile()
    {
        _stream.close();

        // Move the temporary stream over the actual file, removing the target first
        if (fs::exists(_targetFile))
        {
            try
            {
                fs::remove(_targetFile);
            }
            catch (fs::filesystem_error& e)
            {
                rError() << "Could not remove the file " << _targetFile.string() << std::endl
                    << e.what() << std::endl;

                throw std::runtime_error(fmt::format(_("Could not remove the file {0}"), _targetFile.string()));
            }
        }

        try
        {
            fs::rename(_temporaryPath, _targetFile);
        }
        catch (fs::filesystem_error& e)
        {
            rError() << "Could not rename the temporary file " << _temporaryPath.string() << std::endl
                << e.what() << std::endl;

            throw std::runtime_error(
                fmt::format(_("Could not rename the temporary file {0}"), _temporaryPath.string()));
        }
    }

private:
    static fs::path getTemporaryPath(const fs::path& targetFile)
    {
        fs::path tempFile = targetFile;

        tempFile.remove_filename();
        tempFile /= "_" + targetFile.filename().string();

        return tempFile;
    }
};

}