From 530535b2a9fd9cf2688170dfc60d6d455b21d498 Mon Sep 17 00:00:00 2001 From: kayomn Date: Thu, 2 Mar 2023 20:46:25 +0000 Subject: [PATCH] Tidy up turtle API --- source/turtle.cpp | 176 ++++++------------------------------------- source/turtle/io.cpp | 15 ++-- 2 files changed, 29 insertions(+), 162 deletions(-) diff --git a/source/turtle.cpp b/source/turtle.cpp index d68811a..eee9e72 100755 --- a/source/turtle.cpp +++ b/source/turtle.cpp @@ -13,7 +13,6 @@ import coral.io; using coral::closure; using coral::io_error; -using coral::path; using coral::slice; using coral::unreachable; using coral::usize; @@ -336,126 +335,10 @@ export namespace turtle { }; /** - * Provides unmanaged access to a native directory. + * [coral::file_system] wrapper around native file system access to provide a managed and system-agnostic + * environment for performing file I/O. */ - struct native_directory final : public coral::file_walker { - native_directory() = default; - - /** - * Attempts to close any currently open directory. - * - * A [close_result] is returned containing either [close_result::ok] to indicate success or any other value to - * indicate an error. See [close_result] for more details. - * - * *Note*: failing to close should not be treated as a reason to retry the closing operation, and should instead - * be used to inform the end-user that the operation failed or that the process should exit. - */ - close_result close() { - if (closedir(this->dir) == 0) return close_result::io_unavailable; - - return close_result::ok; - } - - /** - * See [coral::file_walker::has_next]. - */ - bool has_next() override { - return this->entry != nullptr; - } - - /** - * Returns `true` if a directory is currently open, otherwise `false`. - */ - bool is_open() const { - return this->dir != nullptr; - } - - /** - * See [coral::file_walker::next]. - */ - coral::expected next() override { - usize name_length {0}; - constexpr usize name_max {sizeof(dirent::d_name) / sizeof(char)}; - - while ((name_length < name_max) && (this->entry->d_name[name_length] != 0)) name_length += 1; - - path current_path {path{}.joined(slice{this->entry->d_name, name_length})}; - - errno = 0; - this->entry = readdir(this->dir); - - if (this->entry == nullptr) switch (errno) { - case EBADF: return io_error::unavailable; - default: unreachable(); - } - - return current_path; - } - - /** - * Attempts to open a native directory at `directory_path`. - * - * An [open_result] is returned containing either [open_result::ok] to indicate success or any other value to - * indicate an error. See [open_result] for more details. - * - * *Note*: the opened directory must be closed using [close] once no longer needed or the process will leak - * directory streams. - * - * *Note*: if a directory is currently open under the native directory, it will attempt to close it before - * proceeding with opening the next. This means that [open] is safe to call without first calling [close]. - * - * *Note*: it is recommended to prefer performing file I/O via [sandboxed_fs] unless direct file access is - * required. - */ - open_result open(native_path const & directory_path) { - if (this->is_open()) { - open_result const result {close_to_open_result(this->close())}; - - if (result != open_result::ok) return result; - } - - // No room for zero terminator. - errno = 0; - this->dir = opendir(directory_path.as_slice().as_chars().pointer); - - if (!this->is_open()) switch (errno) { - case EACCES: return open_result::access_denied; - case EMFILE: case ENFILE: return open_result::too_many; - case ENOENT: case ENOTDIR: return open_result::not_found; - case ENOMEM: return open_result::out_of_memory; - } - - errno = 0; - this->entry = readdir(this->dir); - - if (this->entry == nullptr) switch (errno) { - case EBADF: { - if (this->is_open()) { - open_result const result {close_to_open_result(this->close())}; - - if (result != open_result::ok) return result; - } - - return open_result::io_unavailable; - } - - default: unreachable(); - } - - return open_result::ok; - } - - private: - DIR * dir {nullptr}; - - dirent * entry {nullptr}; - }; - - /** - * [coral::fs] wrapper around native file system access to provide a managed and system-agnostic environment for - * performing file I/O. - */ - struct sandboxed_fs : public coral::fs { + struct file_sandbox : public coral::file_system { /** * Permission flags that a [sandboxed_fs] may specify for restricting access to it. */ @@ -468,16 +351,15 @@ export namespace turtle { }; /** - * Constructs a sandbox located at `sandbox_path` with `sandbox_permissions` as the permissions given to users - * of it. + * Constructs a sandbox located at `path` with `sandbox_permissions` as the permissions given to users of it. */ - sandboxed_fs(native_path const & sandbox_path, permissions const & access_permissions) { - this->path = sandbox_path; + file_sandbox(native_path const & path, permissions const & access_permissions) { + this->path = path; this->access_permissions = access_permissions; } /** - * Returns a reference to a [sandboxed_fs] that provides access to the base directory which, on most systems, is + * Returns a reference to a [file_sandbox] that provides access to the base directory which, on most systems, is * the current working directory. * * The base directory may be used to access things loose files created outside of the application, such as user- @@ -485,17 +367,17 @@ export namespace turtle { * * *Note*: The base file system does not permit being written to. */ - static sandboxed_fs & base() { - static sandboxed_fs base_fs {"./", { + static file_sandbox & base() { + static file_sandbox base_sandbox {"./", { .can_read = true, .can_walk = true }}; - return base_fs; + return base_sandbox; } /** - * Returns a reference to a [sandboxed_fs] that operates as a temporary file store. + * Returns a reference to a [file_sandbox] that operates as a temporary file store. * * As the name implies, the existence of files that exist here are not guaranteed beyond the duration of an * application run lifetime. The purpose of the temporary file store is to support transactional I/O @@ -503,13 +385,13 @@ export namespace turtle { * * *Note*: The temp file system does not permit being walked. */ - static sandboxed_fs & temp() { - static sandboxed_fs base_fs {"/tmp", { + static file_sandbox & temp() { + static file_sandbox temp_sandbox {"/tmp", { .can_read = true, .can_write = false, }}; - return base_fs; + return temp_sandbox; } /** @@ -517,17 +399,16 @@ export namespace turtle { * * *Note*: this function will only work on sandboxes with the [permissions::can_read] flag enabled. */ - void read_file(path const & target_path, closure const & then) override { + void read_file(slice const & path, closure const & then) override { if (!this->access_permissions.can_read) return; - this->path.joined(target_path.as_slice()).and_then([&](native_path const & native_file_path) -> void { + this->path.joined(path).and_then([&](native_path const & native_file_path) -> void { native_file file; if (file.open(native_file_path, native_file::open_mode::read_only) != open_result::ok) return; then(file); - // TODO: Error orphaned file handle! if (file.close() != close_result::ok) return; }); } @@ -537,18 +418,11 @@ export namespace turtle { * * *Note*: this function will only work on sandboxes with the [permissions::can_walk] flag enabled. */ - void walk_files(path const & target_path, closure const & then) override { + void walk_files(slice const & path, closure const & then) override { if (!this->access_permissions.can_walk) return; - this->path.joined(target_path.as_slice()).and_then([&](native_path const & native_directory_path) -> void { - native_directory directory; - - if (directory.open(native_directory_path) == open_result::ok) return; - - then(directory); - - // TODO: Error orphaned file handle! - if (directory.close() != close_result::ok) return; + this->path.joined(path).and_then([&](native_path const & native_directory_path) -> void { + // TODO: Implement. }); } @@ -557,17 +431,16 @@ export namespace turtle { * * *Note*: this function will only work on sandboxes with the [permissions::can_write] flag enabled. */ - void write_file(path const & target_path, closure const & then) override { + void write_file(slice const & path, closure const & then) override { if (!this->access_permissions.can_write) return; - this->path.joined(target_path.as_slice()).and_then([&](native_path const & native_file_path) -> void { + this->path.joined(path).and_then([&](native_path const & native_file_path) -> void { native_file file; if (file.open(native_file_path, native_file::open_mode::overwrite) != open_result::ok) return; then(file); - // TODO: Error orphaned file handle! if (file.close() != close_result::ok) return; }); } @@ -583,12 +456,9 @@ export namespace turtle { }; /** - * Returns a reference to the process-wide output device used for writing data out from to the wider system. - * - * This [coral::writer] is particularly useful for command-line tools which require communicating with another - * process via pipes or an end-user via the shell. + * Returns a reference to the process-wide error logging [coral::writer] used for recording errors. */ - coral::writer & output() { + coral::writer & error_log() { static struct : public coral::writer { coral::expected write(slice const & data) override { coral::size const data_written {::write(STDOUT_FILENO, data.pointer, sizeof(coral::u8) * data.length)}; diff --git a/source/turtle/io.cpp b/source/turtle/io.cpp index 96732c0..c7ba938 100644 --- a/source/turtle/io.cpp +++ b/source/turtle/io.cpp @@ -13,26 +13,23 @@ export namespace turtle { }; struct event_loop { - void log(log_level level, coral::slice const & message) { - static_cast(output().write(message.as_chars().as_bytes())); - static_cast(output().write(coral::slice{"\n"}.as_bytes())); - } - bool poll() { return false; } - static int run(coral::path const & title, coral::closure execute) { + static int run(coral::slice const & title, coral::closure execute) { event_loop loop{title}; return execute(loop); } private: - coral::path title; + static constexpr coral::usize title_max = 256; - event_loop(coral::path const & title) { - this->title = title; + coral::u8 title_buffer[256]; + + event_loop(coral::slice const & title) { + coral::copy(this->title_buffer, title.sliced(0, coral::min(title.length, title_max)).as_bytes()); } };