export module coral.files; import coral; export namespace coral { /** * Platform-generalized identifier for a resource in a [fs]. */ struct path { /** * Maximum path length. */ static usize const max = u8_max; /** * Common path component separator. */ static char const seperator = '/'; constexpr path() { this->buffer[max] = max; } template constexpr path(char const(&text)[text_size]) { static_assert(text_size <= max); for (usize i = 0; i < text_size; i += 1) this->buffer[i] = text[i]; this->buffer[max] = max - text_size; } /** * Returns a weak reference to the [path] as a [slice]. */ constexpr slice as_slice() const { return {this->buffer, this->filled()}; } /** * Returns the base pointer of the path name. * * *Note*: the returned buffer pointer is guaranteed to end with a zero terminator. */ char const * begin() const { return reinterpret_cast(this->buffer); } /** * Compares the path to `that`, returning the difference between the two paths or `0` if they are identical. */ constexpr size compare(path const & that) const { return coral::compare(this->as_slice().as_bytes(), that.as_slice().as_bytes()); } /** * Returns the tail pointer of the path name. */ char const * end() const { return this->buffer + this->filled(); } /** * Tests the path against `that` for equality, returning `true` if they are identical, otherwise `false`. */ constexpr bool equals(path const & that) const { return coral::equals(this->as_slice().as_bytes(), that.as_slice().as_bytes()); } /** * Returns the number of characters composing the path. */ constexpr usize filled() const { return max - this->buffer[max]; } /** * Returns the path hash code. * * *Note:* the returned hash code is not guaranteed to be unique. */ constexpr u64 hash() const { return coral::hash(this->as_slice().as_bytes()); } /** * Returns a new [path] composed of the current path joined with `text`. * * *Note:* should the new path exceed [max] bytes in size, an empty [path] is returned instead. * * *Note:* should the new path exceed [max] bytes in size, an empty [path] is returned instead. */ constexpr path joined(slice const & text) const { if (text.length > this->buffer[max]) return path{}; path joined_path = *this; for (char const c : text) { joined_path.buffer[joined_path.filled()] = c; joined_path.buffer[max] -= 1; } return joined_path; } private: char buffer[max + 1]{0}; }; /** * [reader] that has a known range of data and may attempt to traverse it freely. */ struct file_reader : public reader { /** * Attempts to seek to the position in the file defined by `offset`, returning the literal absolute position * that was actually sought to or a [io_error] if the operation failed for whatever reason. */ virtual expected seek(u64 offset) = 0; /** * Attempts to get the current cursor position in the file, returning the literal absolute position of it or a * [io_error] if the operation failed for whatever reason. */ virtual expected tell() = 0; }; /** * Enumerator a file tree recursively, returning the absolute path of each file within. */ struct file_walker { virtual ~file_walker() {} /** * Returns `true` if there are paths pending enumeration, otherwise `false`. */ virtual bool has_next() = 0; /** * Attempts to enumerate over the next absolute [path] in the file tree, returning it or an [io_error] if the * operation failed for whatever reason. */ virtual expected next() = 0; }; /** * [writer] that has a known range of data and may attempt to traverse it freely. */ struct file_writer : public writer { /** * Attempts to seek to the position in the file defined by `offset`, returning the literal absolute position * that was actually sought to or a [io_error] if the operation failed for whatever reason. */ virtual expected seek(u64 offset) = 0; /** * Attempts to get the current cursor position in the file, returning the literal absolute position of it or a * [io_error] if the operation failed for whatever reason. */ virtual expected tell() = 0; }; /** * Platform-generalized file system interface. */ struct fs { virtual ~fs() {}; /** * Attempts to read the file in `target_path`, calling `then` if it was successfully opened for reading and * passing the [file_reader] context along. * * See [file_reader] for more information on how to read from the file. */ virtual void read_file(path const & target_path, closure const & then) {} /** * Attempts to walk the file tree from `target_path`, calling `then` if it was successfully opened for walking * and passing the [file_walker] context along. * * See [file_walker] for more information on how to traverse the file tree. */ virtual void walk_files(path const & target_path, closure const & then) {} /** * Attempts to write a file in the file system located at `target_path`, calling `then` if it was successfully * created and / or opened for writing and passing the [file_writer] context along. * * See [file_writer] for more information on how to write to the file. * * *Note*: Any file already existing at `target_path` will be overwritten to create a new file for writing. */ virtual void write_file(path const & target_path, closure const & then) {} }; }