aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.posix/write_file.ccm
blob: ce21e6bba431367973cc9359c11c47c4c0b937de (plain) (blame)
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
/*
 * This source code is released into the public domain.
 */

module;

#include <coroutine>
#include <expected>
#include <filesystem>
#include <ranges>
#include <system_error>
#include <vector>

#include <fcntl.h>
#include <unistd.h>

export module nihil.posix:write_file;

import nihil.error;
import nihil.guard;
import nihil.monad;
import :fd;
import :open;
import :rename;

namespace nihil {

/*
 * Write the contents of a range to a file.  Returns the number of bytes
 * written.
 */
export [[nodiscard]]
auto write_file(std::filesystem::path const &filename,
		std::ranges::contiguous_range auto &&range,
		int mode = 0777)
	-> std::expected<std::size_t, error>
{
	auto file = co_await open(filename, open_write | open_create, mode);
	auto nbytes = co_await write(file, range);
	co_return nbytes;
}

/*
 * Utility wrapper for non-contiguous ranges.
 */
export [[nodiscard]]
auto write_file(std::filesystem::path const &filename,
		std::ranges::range auto &&range)
	-> std::expected<std::size_t, error>
requires(!std::ranges::contiguous_range<decltype(range)>)
{
	return write_file(filename, std::vector(std::from_range, range));
}

/*
 * Write the contents of a range to a file safely.  The data will be written
 * to "<filename>.tmp", and if the write succeeds, the temporary file will be
 * renamed to the target filename.  If an error occurs, the target file will
 * not be modified.
 */
export [[nodiscard]]
auto safe_write_file(std::filesystem::path const &filename,
		     std::ranges::range auto &&range)
	-> std::expected<void, error>
{
	auto tmpfile = filename;
	tmpfile.remove_filename();
	tmpfile /= (filename.filename().native() + ".tmp");

	auto tmpfile_guard = guard([&tmpfile] {
		::unlink(tmpfile.c_str());
	});

	co_await write_file(tmpfile, range);
	co_await nihil::rename(tmpfile, filename);

	tmpfile_guard.release();
	co_return {};
}


} // namespace nihil