diff --git a/CMakeLists.txt b/CMakeLists.txt index c278a7f..33c4056 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,10 @@ add_executable(Atlas-Launcher source/script/module/filesystem/_load.hpp source/script/module/filesystem/Path.cpp source/script/module/filesystem/Path.hpp + source/script/module/filesystem/File.cpp + source/script/module/filesystem/File.hpp + source/utils/file/openMode.cpp + source/utils/file/openMode.hpp ) target_include_directories(Atlas-Launcher PRIVATE # AngelScript diff --git a/source/javascript/module/AtlasJsModule.cpp b/source/javascript/module/AtlasJsModule.cpp deleted file mode 100644 index 00f963f..0000000 --- a/source/javascript/module/AtlasJsModule.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "AtlasJsModule.hpp" - -#include "debug/DebugJsModule.hpp" -#include "file_system/FsJsModule.hpp" -#include "image/ImageJsModule.hpp" - - -AtlasJsModule::AtlasJsModule(AtlasJsEngine* engine, QObject* parent) : QObject(parent) { - // set the engine - this->engine = engine; - - // load the submodules - this->submodules[QStringLiteral("debug")] = std::make_shared(this->engine); - this->submodules[QStringLiteral("fs")] = std::make_shared(this->engine); - // this->submodules[QStringLiteral("image")] = std::make_shared(this->engine); - - // TODO(Faraphel): check if the filesystem is correctly restricted -} - -QJSValue AtlasJsModule::require(const QString& name) const { - std::shared_ptr submodule; - - try { - // get the submodule from its name - submodule = this->submodules.at(name); - } catch (const std::out_of_range&) { - // if not found, throw a Javascript error and return null - this->engine->throwError( - QJSValue::ReferenceError, - QStringLiteral("the module \"") + name + QStringLiteral("\" does not exist.") - ); - return QJSValue(QJSPrimitiveNull()); - } - - // return the module as a new javascript object - return this->engine->newQObject(submodule.get()); -} - -QJSValueList AtlasJsModule::getVersion() { - // TODO(Faraphel): should be stored somewhere else. - // TODO(Faraphel): The type should implement an easy comparison system, if possible - QJSValueList version; - - version.append(1); - version.append(0); - version.append(0); - - return version; -} \ No newline at end of file diff --git a/source/javascript/module/AtlasJsModule.hpp b/source/javascript/module/AtlasJsModule.hpp deleted file mode 100644 index 63bc8d5..0000000 --- a/source/javascript/module/AtlasJsModule.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - - -#include -#include - - -class AtlasJsEngine; -class BaseJsModule; - - -class AtlasJsModule : public QObject { - Q_OBJECT - -public: - explicit AtlasJsModule(AtlasJsEngine* engine, QObject* parent = nullptr); - - /** - * Import an Atlas submodule - * @param name the name of the module to import - * @return the module - */ - Q_INVOKABLE QJSValue require(const QString& name) const; - - /** - * Get the version of the Atlas Javascript engine - * @return the Atlas version - */ - Q_INVOKABLE static QList getVersion(); - -private: - /// the parent JavaScript engine - AtlasJsEngine* engine; - /// the submodules contained in this module - std::map> submodules; -}; diff --git a/source/javascript/module/_base/BaseJsModule.cpp b/source/javascript/module/_base/BaseJsModule.cpp deleted file mode 100644 index 5854d74..0000000 --- a/source/javascript/module/_base/BaseJsModule.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "BaseJsModule.hpp" - -#include "javascript/engine/AtlasJsEngine.hpp" - - -BaseJsModule::BaseJsModule(AtlasJsEngine* engine) { - this->engine = engine; -} diff --git a/source/javascript/module/_base/BaseJsModule.hpp b/source/javascript/module/_base/BaseJsModule.hpp deleted file mode 100644 index f911657..0000000 --- a/source/javascript/module/_base/BaseJsModule.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#include "javascript/engine/AtlasJsEngine.hpp" - - -/** - * Base class for an Atlas JavaScript submodule - */ -class BaseJsModule : public QObject { - Q_OBJECT - -public: - explicit BaseJsModule(AtlasJsEngine* engine); - -protected: - /// The javascript engine running the module - AtlasJsEngine* engine; -}; diff --git a/source/javascript/module/file_system/object/FileJsObject.cpp b/source/javascript/module/file_system/object/FileJsObject.cpp deleted file mode 100644 index 8cd3d52..0000000 --- a/source/javascript/module/file_system/object/FileJsObject.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "FileJsObject.hpp" - -#include -#include - -#include "exception/FileNotFoundException.hpp" -#include "javascript/engine/AtlasJsEngine.hpp" -#include "utils/qt/fileOpenMode.hpp" - - -FileJsObject::FileJsObject(AtlasJsEngine* engine, const std::filesystem::path& virtualPath) { - // save the engine - this->engine = engine; - - // create the internal QFile - std::filesystem::path realPath; - try { - realPath = this->engine->getFileSystem().resolve(virtualPath); - } catch (const vfs::exception::FileNotFoundException& exception) { - this->engine->throwError(QJSValue::ErrorType::URIError, "Invalid path."); - return; - } - - // allocate the internal file - this->_internal = std::make_unique(realPath); -} - -void FileJsObject::open(const QString& mode) { - // get the mode flags from its string representation - this->mode = fileModeFromString(mode); - - // open the file with the corresponding mode - if (!this->_internal->open(this->mode)) { - this->engine->throwError(QJSValue::URIError, "Cannot open the file."); - return; - } - - // if we are in text mode, prepare the QTextStream wrapper - if (this->mode.testFlag(QIODevice::Text)) - this->textStream = std::make_unique(this->_internal.get()); -} - -QJSValue FileJsObject::read(const std::size_t size) const { - // check if we can read the file - if (!this->mode.testFlag(QIODevice::ReadOnly)) { - this->engine->throwError(QJSValue::ErrorType::TypeError, "The file is not open in read mode."); - return QJSValue(QJSPrimitiveNull()); - } - - // if we are in text mode, read from the text stream - if (this->mode.testFlag(QIODevice::Text)) - return this->textStream->read(static_cast(size)); - - // if we are in binary mode, read from the binary stream - const auto data = this->_internal->read(static_cast(size)); - return this->engine->toScriptValue(data); -} - -std::size_t FileJsObject::write(const QJSValue& data) const { - // check if we can write into the file - if (!this->mode.testFlag(QIODevice::WriteOnly)) { - this->engine->throwError(QJSValue::ErrorType::TypeError, "The file is not open in write mode."); - return 0; - } - - const QString dataString = data.toString(); - - // if we are in text mode, write text data - if (this->mode.testFlag(QIODevice::Text)) { - *this->textStream << dataString; - return dataString.size(); - } - - const QByteArray dataByte = dataString.toUtf8(); - // if we are in binary mode, insert the raw binary data - return this->_internal->write(dataByte); -} - -std::size_t FileJsObject::tell() const { - // if we are in text mode, return the position of the text stream - if (this->mode.testFlag(QIODevice::Text)) - return this->textStream->pos(); - - // if we are in binary mode, return the position of the raw binary stream - return this->_internal->pos(); -} - -void FileJsObject::seek(const std::size_t position) const { - // if we are in text mode, set the position of the text stream - if (this->mode.testFlag(QIODevice::Text)) { - this->textStream->seek(static_cast(position)); - return; - } - - // if we are in binary mode, set the position of the raw binary stream - this->_internal->seek(static_cast(position)); -} - -void FileJsObject::close() const { - // if still opened, close the file - if (this->_internal->isOpen()) - this->_internal->close(); -} diff --git a/source/javascript/module/file_system/object/FileJsObject.hpp b/source/javascript/module/file_system/object/FileJsObject.hpp deleted file mode 100644 index f5ac47a..0000000 --- a/source/javascript/module/file_system/object/FileJsObject.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "javascript/engine/AtlasJsEngine.hpp" - - -/** - * Represent a JavaScript file. - * This is a wrapper around `QFile` - */ -class FileJsObject : public QObject { - Q_OBJECT - -public: - explicit FileJsObject(AtlasJsEngine* engine, const std::filesystem::path& virtualPath); - - /** - * Open the file to access its content. - * @param mode the opening mode of the file, using "r", "w", "+", etc... - */ - Q_INVOKABLE void open(const QString& mode = QStringLiteral("r")); - - /** - * Read the content of the file. - * @param size the size of the content to read. - * @return the content of the file - */ - Q_INVOKABLE QJSValue read(size_t size = std::numeric_limits::max()) const; - /** - * Write data into the file - * @param data the data to write into the file - * @return the size of the data written - */ - Q_INVOKABLE std::size_t write(const QJSValue& data) const; - - Q_INVOKABLE std::size_t tell() const; - Q_INVOKABLE void seek(std::size_t position) const; - - /** - * Close the file. - */ - Q_INVOKABLE void close() const; - -private: - AtlasJsEngine* engine; - - std::unique_ptr _internal; - QIODevice::OpenMode mode; - std::unique_ptr textStream; -}; diff --git a/source/javascript/module/file_system/object/PathJsObject.cpp b/source/javascript/module/file_system/object/PathJsObject.cpp deleted file mode 100644 index a53cf80..0000000 --- a/source/javascript/module/file_system/object/PathJsObject.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "PathJsObject.hpp" - -#include -#include - -#include "FileJsObject.hpp" -#include "exception/FileNotFoundException.hpp" - - -PathJsObject::PathJsObject(AtlasJsEngine* engine, const std::filesystem::path& virtualPath) { - this->engine = engine; - this->virtualPath = virtualPath; -} - -QJSValue PathJsObject::getParent() const { - // return the parent directory - return this->engine->newQObject(new PathJsObject( - this->engine, - this->virtualPath.parent_path() - )); -} - -QList PathJsObject::getChildrens() const { - // check if the path is iterable - if (!this->isDirectory()) { - this->engine->throwError(QJSValue::ErrorType::TypeError, "Only directories can have children."); - return {}; - } - - // get the corresponding real directory path - const std::filesystem::path realPath = this->getRealPath(); - - // prepare the list of children paths - auto childsPath = QList(); - - // iterate through the childrens of the directory - for (const auto& childRealEntry : std::filesystem::directory_iterator(realPath)) { - // get the corresponding path object - const auto& childRealPath = childRealEntry.path(); - - // get the virtual path - std::filesystem::path childVirtualPath = this->virtualPath / childRealPath.filename(); - // add it to the list of childrens - const auto pathJs = this->engine->newQObject(new PathJsObject( - this->engine, - childVirtualPath - )); - childsPath.append(pathJs); - } - - return childsPath; -} - -bool PathJsObject::isFile() const { - return is_regular_file(this->getRealPath()); -} - -bool PathJsObject::isDirectory() const { - return is_directory(this->getRealPath()); -} - -Q_INVOKABLE QJSValue PathJsObject::open(const QString& mode, const QString& encoding) { - // create a new file object - auto* file = new FileJsObject( - this->engine, - this->virtualPath - ); - // open it with the given mode - file->open(mode); - - // convert it into a QJSValue - auto fileJs = this->engine->newQObject(file); - - return fileJs; - - // TODO(Faraphel): handle encoding -} - -QString PathJsObject::getPath() const { - return QString::fromStdString(this->virtualPath.string()); -} - -QString PathJsObject::getDirname() const { - return QString::fromStdString(this->virtualPath.parent_path().string()); -} - -QString PathJsObject::getFilename() const { - return QString::fromStdString(this->virtualPath.filename()); -} - -QString PathJsObject::getBasename() const { - return QString::fromStdString(this->virtualPath.stem()); -} - -QString PathJsObject::getSuffix() const { - return QString::fromStdString(this->virtualPath.extension()); -} - -std::filesystem::path PathJsObject::getRealPath() const { - return this->engine->getFileSystem().resolve(this->virtualPath); -} diff --git a/source/javascript/module/file_system/object/PathJsObject.hpp b/source/javascript/module/file_system/object/PathJsObject.hpp deleted file mode 100644 index 24d326d..0000000 --- a/source/javascript/module/file_system/object/PathJsObject.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "javascript/module/file_system/object/FileJsObject.hpp" -#include "javascript/engine/AtlasJsEngine.hpp" - - -/** - * Represent a file system Path as a JavaScript object - * Similar to the pathlib library in Python - */ -class PathJsObject : public QObject { - Q_OBJECT - -public: - explicit PathJsObject(AtlasJsEngine* engine, const std::filesystem::path& virtualPath); - - // browse - - /** - * Get the parent directory - * @return the parent directory - */ - Q_INVOKABLE QJSValue getParent() const; - - /** - * Get the child paths - * @return the child paths - */ - Q_INVOKABLE QList getChildrens(bool recursive = false, QString pattern = "*") const; - - // type - - Q_INVOKABLE bool exists(); - - /** - * is the current path leading to a file - * @return is the current path leading to a file - */ - Q_INVOKABLE bool isFile() const; - /** - * is the current path leading to a directory - * @return is the current path leading to a directory - */ - Q_INVOKABLE bool isDirectory() const; - - // operation - - /** - * open the path as a file - * @return the opened file - */ - Q_INVOKABLE QJSValue open(const QString& mode, const QString& encoding = QStringLiteral("utf-8")); - - Q_INVOKABLE void mkdir(bool parents); - - Q_INVOKABLE void remove(bool recursive); - - // component - - /** - * get the full path to the file - * @return the full path to the file - */ - Q_INVOKABLE QString getPath() const; - /** - * get the name of the directory containing the file - * @return the name of the directory containing the file - */ - Q_INVOKABLE QString getDirname() const; - /** - * get the name of the file - * @return the name of the file - */ - Q_INVOKABLE QString getFilename() const; - /** - * get the name of the file without the suffix - * @return the name of the file without the suffix - */ - Q_INVOKABLE QString getBasename() const; - /** - * get the extension of the file - * @return the extension of the file - */ - Q_INVOKABLE QString getSuffix() const; - /** - * get the extensions of the file - * @return the extensions of the file - */ - Q_INVOKABLE QList getSuffixes() const; - /** - * get the segments of the path - * @return the segments of the path - */ - Q_INVOKABLE QList getSegments() const; - -private: - /// the JavaScript engine - AtlasJsEngine* engine; - /// the virtual path - std::filesystem::path virtualPath; - - /** - * Get the real path on the host file system of the path - * @return the real path on the host file system - * @throw rvfs::exception::FileNotFoundException if the path does not match with anything on the real file system - */ - std::filesystem::path getRealPath() const; -}; diff --git a/source/main.cpp b/source/main.cpp index 39b91af..9f54d5d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -8,6 +8,7 @@ #include "script/engine/Engine.hpp" #include "script/module/debug/_load.hpp" +#include "script/module/filesystem/_load.hpp" int main_AngelScript(int argc, char* argv[]) { @@ -19,6 +20,7 @@ int main_AngelScript(int argc, char* argv[]) { // functions atlas::script::module::debug::load(atlasEngine.get()); + atlas::script::module::filesystem::load(atlasEngine.get()); // script CScriptBuilder builder; @@ -27,12 +29,7 @@ int main_AngelScript(int argc, char* argv[]) { if (error < 0) throw std::runtime_error("Could not start a new module."); - error = builder.AddSectionFromMemory("script", R"( - void main() { - array data(256); - atlas::debug::log("main", "hello world !"); - } - )"); + error = builder.AddSectionFromFile(".mods/Atlas/scripts/hello.as"); if (error < 0) throw std::runtime_error("Could not add a section to the module."); @@ -88,5 +85,5 @@ int main_vfspp(int argc, char* argv[]) { } int main(int argc, char* argv[]) { - main_vfspp(argc, argv); + main_AngelScript(argc, argv); } diff --git a/source/script/engine/Engine.cpp b/source/script/engine/Engine.cpp index ce4ecb2..ff9d9ce 100644 --- a/source/script/engine/Engine.cpp +++ b/source/script/engine/Engine.cpp @@ -38,6 +38,9 @@ Engine::Engine() { RegisterScriptWeakRef(this->asEngine); RegisterExceptionRoutines(this->asEngine); RegisterScriptMath(this->asEngine); + + // Virtual file system + this->fileSystem = std::make_unique(); } void Engine::asCallback(const asSMessageInfo *message, void *args) { @@ -79,4 +82,8 @@ asIScriptEngine* Engine::getAsEngine() const { return this->asEngine; } +std::shared_ptr Engine::getFileSystem() const { + return this->fileSystem; +} + } diff --git a/source/script/engine/Engine.hpp b/source/script/engine/Engine.hpp index 84cb720..507c772 100644 --- a/source/script/engine/Engine.hpp +++ b/source/script/engine/Engine.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace atlas::script::engine { @@ -27,8 +28,15 @@ public: */ [[nodiscard]] asIScriptEngine* getAsEngine() const; + /** + * get the file system + * @return the file system + */ + [[nodiscard]] std::shared_ptr getFileSystem() const; + private: asIScriptEngine* asEngine; + std::shared_ptr fileSystem; }; } diff --git a/source/script/module/filesystem/File.cpp b/source/script/module/filesystem/File.cpp new file mode 100644 index 0000000..65aa550 --- /dev/null +++ b/source/script/module/filesystem/File.cpp @@ -0,0 +1,65 @@ +#include "File.hpp" + +#include "utils/file/openMode.hpp" + + +File::File(const atlas::script::engine::Engine* engine, const std::filesystem::path& virtualPath) { + this->engine = engine; + this->virtualPath = virtualPath; +} + +void File::open(const std::string& mode) { + // allocate the internal file + this->stream = std::fstream(this->getRealPath(), atlas::utils::file::getOpenMode(mode)); +} + +std::vector File::read(const std::size_t size) { + // create a buffer + std::vector buffer(size); + + // read into the buffer + this->stream.read( + reinterpret_cast(buffer.data()), + static_cast(size) + ); + + // set the write cursor to the read cursor (synchronise) + this->stream.seekp(this->stream.tellg()); + + return buffer; +} + +void File::write(const std::vector& buffer) { + // read onto the buffer + this->stream.write( + reinterpret_cast(buffer.data()), + static_cast(buffer.size()) + ); + + // set the read cursor to the write cursor (synchronise) + this->stream.seekg(this->stream.tellp()); +} + +std::size_t File::tell() { + // return the stream cursor position. + // The read and write cursors are always synchronised. + return this->stream.tellp(); +} + +void File::seek(const std::size_t position) { + // set the new stream cursor position. + // synchronise the two cursors. + this->stream.seekg(static_cast(position)); + this->stream.seekp(this->stream.tellg()); +} + +void File::close() { + // close the stream + this->stream.close(); +} + +std::filesystem::path File::getRealPath() const { + // get the real path from the internal virtual path + return this->engine->getFileSystem()->resolve(this->virtualPath); +} + diff --git a/source/script/module/filesystem/File.hpp b/source/script/module/filesystem/File.hpp new file mode 100644 index 0000000..e8a10ca --- /dev/null +++ b/source/script/module/filesystem/File.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include + +#include "script/engine/Engine.hpp" + + +/** + * Represent a JavaScript file. + * This is a wrapper around `QFile` + */ +class File { + +public: + explicit File(const atlas::script::engine::Engine* engine, const std::filesystem::path& virtualPath); + + /** + * Open the file to access its content. + * @param mode the opening mode of the file, using "r", "w", "+", etc... + */ + void open(const std::string& mode = "r"); + + /** + * Read the content of the file. + * @param size the size of the content to read. + * @return the content of the file + */ + std::vector read(size_t size = std::numeric_limits::max()); + /** + * Write data into the file + * @param buffer the data to write into the file + * @return the size of the data written + */ + void write(const std::vector& buffer); + + /** + * Get the current cursor position + * @return the current cursor position + */ + std::size_t tell(); + /** + * Set the cursor position + * @param position the new cursor position + */ + void seek(std::size_t position); + + /** + * Close the file. + */ + void close(); + +private: + /** + * Get the corresponding real file path + * @return the real file path + */ + std::filesystem::path getRealPath() const; + + std::filesystem::path virtualPath; + /// the Atlas engine + const atlas::script::engine::Engine* engine; + /// the internal file streamer + std::fstream stream; +}; diff --git a/source/script/module/filesystem/Path.cpp b/source/script/module/filesystem/Path.cpp index 3bd542d..61286ea 100644 --- a/source/script/module/filesystem/Path.cpp +++ b/source/script/module/filesystem/Path.cpp @@ -1,8 +1,131 @@ #include "Path.hpp" -namespace atlas::script::module::filesystem { +#include -Path::Path(const engine::Engine* atlasEngine, const std::string &path) { +#include "exception/FileNotFoundException.hpp" + + +Path::Path(const atlas::script::engine::Engine* engine, const std::filesystem::path& virtualPath) { + this->engine = engine; + this->virtualPath = virtualPath; } +Path Path::getParent() const { + // return the parent directory + return Path( + this->engine, + this->virtualPath.parent_path() + ); +} + +std::vector> Path::getChildrens(const bool recursive, const std::string& pattern) const { + // TODO(Faraphel): implement recursive and pattern + + // check if the path is iterable + if (!this->isDirectory()) + throw std::runtime_error("Can only iterate directories."); + + // get the corresponding real directory path + const std::filesystem::path realPath = this->getRealPath(); + + // prepare the list of children paths + auto childsPath = std::vector>(); + + // iterate through the childrens of the directory + for (const auto& childRealEntry : std::filesystem::directory_iterator(realPath)) { + // get the corresponding path object + const auto& childRealPath = childRealEntry.path(); + + // get the virtual path + std::filesystem::path childVirtualPath = this->virtualPath / childRealPath.filename(); + // add it to the list of childrens + auto path = std::make_shared( + this->engine, + childVirtualPath + ); + childsPath.push_back(path); + } + + return childsPath; +} + +bool Path::exists() const { + return std::filesystem::exists(this->getRealPath()); +} + +bool Path::isFile() const { + return is_regular_file(this->getRealPath()); +} + +bool Path::isDirectory() const { + return is_directory(this->getRealPath()); +} + +std::shared_ptr Path::open(const std::string& mode, const std::string& encoding) { + // create a new file object + auto file = std::make_shared( + this->engine, + this->virtualPath + ); + // open it with the given mode + file->open(mode); + + return file; + + // TODO(Faraphel): handle encoding. Wrapper text reader like in C ? readText / writeText ? overloading with std::string ? +} + +bool Path::mkdir(const bool parents) const { + return create_directory(this->getRealPath()); +} + +void Path::remove(const bool recursive) const { + if (recursive) + remove_all(this->getRealPath()); + else + std::filesystem::remove(this->getRealPath()); +} + +std::string Path::getPath() const { + return this->virtualPath.string(); +} + +std::string Path::getDirname() const { + return this->virtualPath.parent_path().string(); +} + +std::string Path::getFilename() const { + return this->virtualPath.filename(); +} + +std::string Path::getBasename() const { + return this->virtualPath.stem(); +} + +std::string Path::getSuffix() const { + return this->virtualPath.extension(); +} + +std::vector Path::getSuffixes() const { + const std::string filename = this->virtualPath; + auto delimiter = std::regex("."); + + // split the string at every occurence of the "." character + auto parts = std::vector( + std::sregex_token_iterator(filename.begin(), filename.end(), delimiter, -1), + std::sregex_token_iterator() + ); + + // remove the first string (the basename) + parts.erase(parts.begin()); + + return parts; +} + +std::vector Path::getComponents() const { + return {this->virtualPath.begin(), this->virtualPath.end()}; +} + +std::filesystem::path Path::getRealPath() const { + return this->engine->getFileSystem()->resolve(this->virtualPath); } \ No newline at end of file diff --git a/source/script/module/filesystem/Path.hpp b/source/script/module/filesystem/Path.hpp index e6f8c45..b03b14d 100644 --- a/source/script/module/filesystem/Path.hpp +++ b/source/script/module/filesystem/Path.hpp @@ -1,17 +1,114 @@ #pragma once -#include +#include +#include + +#include "File.hpp" #include "script/engine/Engine.hpp" -namespace atlas::script::module::filesystem { - /** - * Represent a path in the virtual file system + * Represent a virtual file system path + * Similar to the pathlib library in Python */ class Path { -public: - explicit Path(const engine::Engine* atlasEngine, const std::string& path); -}; -} +public: + explicit Path(const atlas::script::engine::Engine* engine, const std::filesystem::path& virtualPath); + + // browse + + /** + * Get the parent directory + * @return the parent directory + */ + [[nodiscard]] Path getParent() const; + + /** + * Get the child paths + * @return the child paths + */ + [[nodiscard]] std::vector> getChildrens(bool recursive = false, const std::string& pattern = "*") const; + + // type + + /** + * Check if the path correspond to a file system entry + * @return does the path exists + */ + [[nodiscard]] bool exists() const; + + /** + * is the current path leading to a file + * @return is the current path leading to a file + */ + [[nodiscard]] bool isFile() const; + /** + * is the current path leading to a directory + * @return is the current path leading to a directory + */ + [[nodiscard]] bool isDirectory() const; + + // operation + + /** + * open the path as a file + * @return the opened file + */ + std::shared_ptr open(const std::string& mode, const std::string& encoding = "utf-8"); + + [[nodiscard]] bool mkdir(bool parents) const; + + void remove(bool recursive) const; + + // component + + /** + * get the full path to the file + * @return the full path to the file + */ + [[nodiscard]] std::string getPath() const; + /** + * get the name of the directory containing the file + * @return the name of the directory containing the file + */ + [[nodiscard]] std::string getDirname() const; + /** + * get the name of the file + * @return the name of the file + */ + [[nodiscard]] std::string getFilename() const; + /** + * get the name of the file without the suffix + * @return the name of the file without the suffix + */ + [[nodiscard]] std::string getBasename() const; + /** + * get the extension of the file + * @return the extension of the file + */ + [[nodiscard]] std::string getSuffix() const; + /** + * get the extensions of the file + * @return the extensions of the file + */ + [[nodiscard]] std::vector getSuffixes() const; + /** + * get the components of the path + * @return the components of the path + */ + [[nodiscard]] std::vector getComponents() const; + +private: + /// the JavaScript engine + const atlas::script::engine::Engine* engine; + /// the virtual path + std::filesystem::path virtualPath; + + /** + * Get the real path on the host file system of the path + * @return the real path on the host file system + * @throw rvfs::exception::FileNotFoundException if the path does not match with anything on the real file system + */ + [[nodiscard]] std::filesystem::path getRealPath() const; +}; diff --git a/source/script/module/filesystem/_load.cpp b/source/script/module/filesystem/_load.cpp index 178ee28..b8f9538 100644 --- a/source/script/module/filesystem/_load.cpp +++ b/source/script/module/filesystem/_load.cpp @@ -2,11 +2,18 @@ #include +#include "Path.hpp" + namespace atlas::script::module::filesystem { +Path* pathFactory(const engine::Engine* atlasEngine, const std::string& virtualPath) { + return new Path(atlasEngine, virtualPath); +}; + void load(const engine::Engine* atlasEngine) { asIScriptEngine* asEngine = atlasEngine->getAsEngine(); + auto fileSystem = atlasEngine->getFileSystem(); int error; // start namespace @@ -14,6 +21,14 @@ void load(const engine::Engine* atlasEngine) { if (error < 0) throw std::runtime_error("Could not enter the \"atlas::filesystem\" namespace."); + // TODO(Faraphel): register the Path and File types + error = asEngine->RegisterGlobalProperty("int Path::FileSystem", fileSystem.get()); + + // error = asEngine->RegisterObjectType("Path", 0, asOBJ_REF); + // error = asEngine->RegisterObjectBehaviour("Path", asBEHAVE_FACTORY, "Path@ f(const string& in)", asFUNCTION(pathFactory), asCALL_CDECL); + // error = asEngine->RegisterObjectMethod("Path", "Path@ getParent() const", asMETHOD(Path, getParent), asCALL_THISCALL); + // error = asEngine->RegisterObjectMethod("Path", "bool exists() const", asMETHOD(Path, exists), asCALL_THISCALL); + // end namespace error = asEngine->SetDefaultNamespace(""); if (error < 0) diff --git a/source/utils/file/openMode.cpp b/source/utils/file/openMode.cpp new file mode 100644 index 0000000..d8d380c --- /dev/null +++ b/source/utils/file/openMode.cpp @@ -0,0 +1,20 @@ +#include "openMode.hpp" + + + +namespace atlas::utils::file { + +std::ios_base::openmode getOpenMode(const std::string& mode) { + // initialise an empty flags + auto flags = static_cast(0); + + // set the flags + if (mode.find('r') != std::string::npos) flags |= std::ios::in; + if (mode.find('w') != std::string::npos) flags |= std::ios::out; + if (mode.find('a') != std::string::npos) flags |= std::ios::app; + if (mode.find('b') != std::string::npos) flags |= std::ios::binary; + + return flags; +} + +} \ No newline at end of file diff --git a/source/utils/file/openMode.hpp b/source/utils/file/openMode.hpp new file mode 100644 index 0000000..df3c00c --- /dev/null +++ b/source/utils/file/openMode.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + + +namespace atlas::utils::file { + +std::ios_base::openmode getOpenMode(const std::string& mode); + +}