diff --git a/docs/source/api/file.rst b/docs/source/api/file.rst new file mode 100644 index 0000000..cbd279c --- /dev/null +++ b/docs/source/api/file.rst @@ -0,0 +1,180 @@ +file +==== + +File operations module for working with assets and the file system. All file operations return two values: a success boolean and an error message string. On success, returns ``true, nil``. On failure, returns ``false, "error message"``. + +.. lua:module:: file + +.. lua:function:: copy(source, destination) + + Copies a file or folder from one location to another. When copying to an asset library (folder), the file maintains its original name in the destination. Fails if the source does not exist or if the destination already exists. + + :param source: Source file or folder to copy + :type source: assetKey or assetLibrary + :param destination: Destination path or folder + :type destination: assetKey or assetLibrary + :return: Success status and error message + :rtype: boolean, string or nil + + .. code-block:: lua + + -- Copy a file to documents with a new name + local ok, err = file.copy(asset.builtin.Pack.File, asset.documents .. "MyFile.png") + if not ok then + print("Copy failed:", err) + end + + -- Copy a file to documents (keeps original name) + local ok, err = file.copy(asset.builtin.Pack.File, asset.documents) + + -- Copy an entire folder + local ok, err = file.copy(asset.builtin.Pack, asset.documents .. "MyPack") + +.. lua:function:: move(source, destination) + + Moves a file or folder from one location to another. When moving to an asset library (folder), the file maintains its original name in the destination. Fails if the source does not exist or if the destination already exists. + + :param source: Source file or folder to move + :type source: assetKey or assetLibrary + :param destination: Destination path or folder + :type destination: assetKey or assetLibrary + :return: Success status and error message + :rtype: boolean, string or nil + + .. code-block:: lua + + -- Move a file to documents with a new name + local ok, err = file.move(asset.icloud.Pack.File, asset.documents .. "MyFile.png") + if not ok then + print("Move failed:", err) + end + + -- Move a file to documents (keeps original name) + local ok, err = file.move(asset.icloud.Pack.File, asset.documents) + + -- Move an entire folder + local ok, err = file.move(asset.icloud.Pack, asset.documents .. "MyPack") + +.. lua:function:: remove(asset) + + Removes a file or folder. Built-in folders and top-level asset packs (asset, asset.builtin, asset.documents, asset.icloud) cannot be removed. Fails if the file does not exist. + + :param asset: File or folder to remove + :type asset: assetKey or assetLibrary + :return: Success status and error message + :rtype: boolean, string or nil + + .. code-block:: lua + + -- Remove a file + local ok, err = file.remove(asset.documents.File) + if not ok then + print("Remove failed:", err) + end + + -- Remove a folder and all its contents + local ok, err = file.remove(asset.documents.SomeFolder) + +.. lua:function:: mkdir(asset) + + Creates a new directory. Fails if the directory already exists or if the parent directory does not exist. + + :param asset: Path where the directory should be created + :type asset: assetKey or assetLibrary + :return: Success status and error message + :rtype: boolean, string or nil + + .. code-block:: lua + + -- Create a new folder in documents + local ok, err = file.mkdir(asset.documents .. "MyFolder") + if not ok then + print("Create directory failed:", err) + end + + -- Will fail if parent doesn't exist + local ok, err = file.mkdir(asset.documents .. "Parent/Child") + -- First create parent, then child + file.mkdir(asset.documents .. "Parent") + file.mkdir(asset.documents .. "Parent/Child") + +.. lua:function:: rename(asset, newName) + + Renames a file or folder within the same parent directory. This is a synonym for moving within the same parent folder to a new name. Fails if the source does not exist or a sibling with the new name already exists. + + :param asset: Source file or folder to rename + :type asset: assetKey + :param newName: The new name for the file or folder + :type newName: string + :return: Success status and error message + :rtype: boolean, string or nil + + .. code-block:: lua + + -- Rename a file within documents + local ok, err = file.rename(asset.documents .. "Old.png", "New.png") + if not ok then + print("Rename failed:", err) + end + + -- Rename a folder within documents + local ok, err = file.rename(asset.documents .. "OldFolder", "NewFolder") + +.. lua:function:: exists(asset) + + Checks if a file or folder exists at the given path. + + :param asset: Path to check for existence + :type asset: assetKey or assetLibrary + :return: True if the path exists, false otherwise + :rtype: boolean + + .. code-block:: lua + + -- Check if a configuration file exists + if file.exists(asset.documents .. "Config.json") then + print("Config is present") + else + print("Missing Config.json") + end + + -- Check if a folder exists + if file.exists(asset.documents .. "Screenshots") then + print("Screenshots folder exists") + end + +**Common Error Messages** + +The file operations return specific error messages for different failure conditions: + +* ``"No such file or directory."`` - The source file or folder does not exist +* ``"Destination already exists. Remove it first."`` - The destination already exists and operations don't overwrite +* ``"Cannot remove built-in or top-level asset packs."`` - Attempting to remove protected system folders +* ``"Parent folder does not exist."`` - Parent directory must exist when creating folders or copying files +* ``"Cannot move item: source and destination are identical."`` - Source and destination paths are the same +* ``"A file or directory with that name already exists."`` - Directory creation failed due to existing item + +**Usage Patterns** + +.. code-block:: lua + + -- Helper function for error checking + local function checkResult(ok, err, operation) + if not ok then + print(operation .. " failed:", err) + return false + end + return true + end + + -- Example usage with error handling + local ok, err = file.copy(asset.Main, asset.documents .. "Backup.lua") + if checkResult(ok, err, "Backup creation") then + print("Backup created successfully") + end + + -- Verify operations with file.exists + if file.exists(asset.documents .. "Backup.lua") then + local ok, err = file.remove(asset.documents .. "Backup.lua") + checkResult(ok, err, "Cleanup") + end \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index dad201c..b3297ab 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -30,6 +30,7 @@ Codea 4 manual/drawing manual/sound manual/assets + manual/file_operations manual/input manual/physics2d manual/physics3d @@ -65,6 +66,7 @@ Codea 4 api/require api/objc api/pasteboard + api/file api/physics2d api/physics3d api/pick diff --git a/docs/source/manual/file_operations.rst b/docs/source/manual/file_operations.rst new file mode 100644 index 0000000..b2798bf --- /dev/null +++ b/docs/source/manual/file_operations.rst @@ -0,0 +1,219 @@ +File Operations +=============== + +Working with Files in Codea +---------------------------- + +Codea provides a comprehensive file operations API through the :lua:mod:`file` module that allows you to manage assets, create directories, and organize your project files. Unlike traditional file APIs, Codea's file operations are designed around the asset system and return status values rather than throwing exceptions. + +All file operations return two values: a success boolean and an error message string. On success, operations return ``true, nil``. On failure, they return ``false, "error message"``. This pattern allows for robust error handling and makes it easy to build reliable applications. + +Basic File Operations +--------------------- + +The file module provides five core operations: copying, moving, removing, creating directories, and renaming files. These operations work with both individual files and entire folders. + +**Copying Files** + +The :lua:func:`file.copy` function duplicates files and folders from one location to another: + +.. code-block:: lua + + -- Copy a file with a new name + local ok, err = file.copy(asset.Main, asset.documents .. "MainBackup.lua") + if not ok then + print("Copy failed:", err) + end + + -- Copy a file to a directory (keeps original name) + file.copy(asset.builtin.Platformer_Art.Player_01, asset.documents) + +When copying to an asset library (a folder), the file automatically maintains its original filename in the destination. + +**Moving and Renaming Files** + +Files can be moved between locations using :lua:func:`file.move`, or renamed within the same directory using :lua:func:`file.rename`: + +.. code-block:: lua + + -- Move a file to a new location + file.move(asset.documents .. "OldLocation.lua", asset.documents .. "NewLocation.lua") + + -- Rename a file within the same directory + file.rename(asset.documents .. "OldName.lua", "NewName.lua") + +**Creating Directories** + +New directories are created with :lua:func:`file.mkdir`. The parent directory must exist before creating subdirectories: + +.. code-block:: lua + + -- Create a screenshots directory + file.mkdir(asset.documents .. "Screenshots") + + -- Create nested directories (parent must exist first) + file.mkdir(asset.documents .. "Projects") + file.mkdir(asset.documents .. "Projects/GameData") + +**Removing Files and Directories** + +The :lua:func:`file.remove` function deletes both files and directories: + +.. code-block:: lua + + -- Remove a single file + file.remove(asset.documents .. "TempFile.txt") + + -- Remove a directory and all its contents + file.remove(asset.documents .. "TempFolder") + +Asset System Integration +------------------------ + +Codea's file operations are tightly integrated with the asset system, allowing you to work with files across different asset locations: + +- **asset** - Your current project files +- **asset.documents** - User documents folder +- **asset.builtin** - Built-in Codea assets (read-only) +- **asset.icloud** - iCloud documents (when available) + +.. code-block:: lua + + -- Copy from builtin assets to your project + file.copy(asset.builtin.Planet_Cute.Heart, asset .. "Heart.png") + + -- Organize project files + file.mkdir(asset .. "Images") + file.move(asset .. "Heart.png", asset .. "Images/Heart.png") + +The asset system automatically handles path resolution and ensures operations work correctly across different storage locations. + +Error Handling Patterns +------------------------ + +Robust applications should always check the return values from file operations. Here are some common patterns: + +**Simple Error Checking** + +.. code-block:: lua + + local ok, err = file.copy(source, destination) + if not ok then + print("Operation failed:", err) + return + end + -- Continue with success case + +**Helper Function Pattern** + +.. code-block:: lua + + local function safeFileOp(operation, ...) + local ok, err = operation(...) + if not ok then + error("File operation failed: " .. tostring(err)) + end + return true + end + + -- Usage + safeFileOp(file.copy, asset.Main, asset.documents .. "Backup.lua") + safeFileOp(file.mkdir, asset.documents .. "NewFolder") + +**Conditional Operations** + +.. code-block:: lua + + -- Only create directory if it doesn't exist + if not file.exists(asset.documents .. "Cache") then + local ok, err = file.mkdir(asset.documents .. "Cache") + if not ok then + print("Failed to create cache directory:", err) + end + end + + -- Safe cleanup - remove only if exists + if file.exists(asset.documents .. "TempFile.txt") then + file.remove(asset.documents .. "TempFile.txt") + end + +Working with Folders +--------------------- + +File operations can work with entire folders, making it easy to organize and restructure your project assets: + +.. code-block:: lua + + -- Copy an entire asset pack to documents + file.copy(asset.builtin.Space_Art, asset.documents .. "SpaceAssets") + + -- Reorganize project structure + file.mkdir(asset .. "Source") + file.mkdir(asset .. "Assets") + + -- Move Lua files to Source folder + for _, fileName in ipairs({"Main.lua", "Game.lua", "Menu.lua"}) do + if file.exists(asset .. fileName) then + file.move(asset .. fileName, asset .. "Source/" .. fileName) + end + end + +When copying folders, the entire directory structure is preserved, including all subdirectories and files. + +Security and Limitations +------------------------- + +The file system has built-in protections to prevent accidental damage to system files: + +**Protected Locations** + +- Top-level asset packs (``asset``, ``asset.builtin``, ``asset.documents``, ``asset.icloud``) cannot be removed +- Built-in assets are read-only and cannot be modified +- Operations that would overwrite existing files fail by default + +**Common Error Conditions** + +- ``"No such file or directory."`` - Source doesn't exist +- ``"Destination already exists. Remove it first."`` - Target already exists +- ``"Cannot remove built-in or top-level asset packs."`` - Protected location +- ``"Parent folder does not exist."`` - Directory structure issue + +**Best Practices** + +- Always check if files exist before operating on them with :lua:func:`file.exists` +- Use descriptive error messages when operations fail +- Clean up temporary files and directories when done +- Test file operations during development to ensure they work as expected + +.. code-block:: lua + + -- Example: Safe project backup function + function backupProject() + local timestamp = os.date("%Y%m%d_%H%M%S") + local backupDir = asset.documents .. "Backups/Project_" .. timestamp + + -- Create backup directory + local ok, err = file.mkdir(asset.documents .. "Backups") + if not ok and not string.find(err, "already exists") then + print("Failed to create backup folder:", err) + return false + end + + ok, err = file.mkdir(backupDir) + if not ok then + print("Failed to create timestamped backup:", err) + return false + end + + -- Copy all project files + ok, err = file.copy(asset, backupDir) + if not ok then + print("Failed to backup project:", err) + return false + end + + print("Project backed up to:", backupDir) + return true + end + +See :doc:`/api/file` for the complete API reference with detailed function descriptions and parameters. \ No newline at end of file