aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.posix/write_file.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.posix/write_file.ccm')
-rw-r--r--nihil.posix/write_file.ccm82
1 files changed, 82 insertions, 0 deletions
diff --git a/nihil.posix/write_file.ccm b/nihil.posix/write_file.ccm
new file mode 100644
index 0000000..867e0db
--- /dev/null
+++ b/nihil.posix/write_file.ccm
@@ -0,0 +1,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, O_CREAT|O_WRONLY, 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