Skip to main content

usage-rules.md

# Jido.VFS Usage Rules

Filesystem abstraction for Elixir with unified interface over multiple backends.

## Filesystem Creation

```elixir
# Direct filesystem configuration
filesystem = Jido.VFS.Adapter.Local.configure(prefix: "/home/user/storage")

# Module-based filesystem (recommended for reuse)
defmodule MyStorage do
  use Jido.VFS.Filesystem,
    adapter: Jido.VFS.Adapter.Local,
    prefix: "/home/user/storage"
end
```

## Basic Operations

```elixir
# Write
:ok = Jido.VFS.write(filesystem, "test.txt", "Hello World")

# Read
{:ok, content} = Jido.VFS.read(filesystem, "test.txt")

# Delete
:ok = Jido.VFS.delete(filesystem, "test.txt")

# Copy
:ok = Jido.VFS.copy(filesystem, "source.txt", "dest.txt")

# Move
:ok = Jido.VFS.move(filesystem, "old.txt", "new.txt")

# Check existence
{:ok, :exists} = Jido.VFS.file_exists(filesystem, "test.txt")
{:ok, :missing} = Jido.VFS.file_exists(filesystem, "nonexistent.txt")

# List contents
{:ok, entries} = Jido.VFS.list_contents(filesystem, "subdir/")

# Get file info
{:ok, stat} = Jido.VFS.stat(filesystem, "test.txt")
```

## Adapters

### Local Filesystem

```elixir
filesystem = Jido.VFS.Adapter.Local.configure(prefix: "/path/to/storage")
```

### In-Memory (Testing)

```elixir
filesystem = Jido.VFS.Adapter.InMemory.configure(name: :test_fs)
```

### ETS-Backed

```elixir
filesystem = Jido.VFS.Adapter.ETS.configure(name: :persistent_fs)
```

### S3 / Minio

```elixir
filesystem = Jido.VFS.Adapter.S3.configure(
  bucket: "my-bucket",
  prefix: "uploads/",
  access_key_id: "...",
  secret_access_key: "..."
)
```

### Git Repository

```elixir
# Manual commit mode
filesystem = Jido.VFS.Adapter.Git.configure(
  path: "/path/to/repo",
  mode: :manual,
  author: [name: "Bot", email: "bot@example.com"]
)

# Auto-commit mode
filesystem = Jido.VFS.Adapter.Git.configure(
  path: "/path/to/repo",
  mode: :auto
)
```

### GitHub API

```elixir
# Read-only access
filesystem = Jido.VFS.Adapter.GitHub.configure(
  owner: "octocat",
  repo: "Hello-World",
  ref: "main"
)

# Authenticated access for writes
filesystem = Jido.VFS.Adapter.GitHub.configure(
  owner: "username",
  repo: "repo-name",
  ref: "main",
  auth: %{access_token: "ghp_..."},
  commit_info: %{
    message: "Update via Jido.VFS",
    committer: %{name: "Name", email: "email@example.com"},
    author: %{name: "Name", email: "email@example.com"}
  }
)
```

## Versioning (Git, ETS, InMemory)

```elixir
# Commit changes (manual mode)
Jido.VFS.write(filesystem, "file.txt", "content")
:ok = Jido.VFS.commit(filesystem, "Add new file")

# List revisions
{:ok, revisions} = Jido.VFS.revisions(filesystem, "file.txt")

# Read historical version
{:ok, old_content} = Jido.VFS.read_revision(filesystem, "file.txt", revision_id)

# Rollback
:ok = Jido.VFS.rollback(filesystem, revision_id)
```

## Streaming

```elixir
# Read stream
{:ok, stream} = Jido.VFS.read_stream(filesystem, "large-file.bin", chunk_size: 65536)
Enum.each(stream, fn chunk -> process(chunk) end)

# Write stream
{:ok, stream} = Jido.VFS.write_stream(filesystem, "output.bin")
data |> Stream.into(stream) |> Stream.run()
```

## Cross-Filesystem Copy

```elixir
source_fs = Jido.VFS.Adapter.Local.configure(prefix: "/source")
dest_fs = Jido.VFS.Adapter.S3.configure(bucket: "dest-bucket")

:ok = Jido.VFS.copy_between_filesystem(
  {source_fs, "file.txt"},
  {dest_fs, "uploaded.txt"}
)
```

## Visibility

```elixir
# Set file visibility
:ok = Jido.VFS.set_visibility(filesystem, "file.txt", :public)
:ok = Jido.VFS.set_visibility(filesystem, "file.txt", :private)

# Get visibility
{:ok, :public} = Jido.VFS.visibility(filesystem, "file.txt")

# Write with visibility
:ok = Jido.VFS.write(filesystem, "file.txt", "content", visibility: :public)
```

## Directories

```elixir
# Create directory
:ok = Jido.VFS.create_directory(filesystem, "new-folder")

# Delete directory
:ok = Jido.VFS.delete_directory(filesystem, "old-folder")

# Clear all contents
:ok = Jido.VFS.clear(filesystem)
```

## Error Handling

```elixir
case Jido.VFS.read(filesystem, "file.txt") do
  {:ok, content} -> process(content)
  {:error, %Jido.VFS.Errors.FileNotFound{}} -> handle_missing()
  {:error, %Jido.VFS.Errors.PathTraversal{}} -> handle_security_error()
  {:error, reason} -> handle_error(reason)
end
```

## Anti-Patterns

**❌ Avoid:**
- Absolute paths: `Jido.VFS.read(fs, "/etc/passwd")`
- Path traversal: `Jido.VFS.read(fs, "../../../etc/passwd")`
- Ignoring errors: `Jido.VFS.write(fs, path, content)`
- Direct file operations: `File.read!(path)`

**✅ Use:**
- Relative paths: `Jido.VFS.read(fs, "documents/file.txt")`
- Error handling: `case Jido.VFS.read(...) do`
- Filesystem abstraction for all file operations
- Module-based filesystems for reusable configurations

## Testing

```elixir
# Use InMemory adapter for tests
setup do
  filesystem = Jido.VFS.Adapter.InMemory.configure(name: :test_fs)
  {:ok, filesystem: filesystem}
end

test "writes and reads file", %{filesystem: fs} do
  :ok = Jido.VFS.write(fs, "test.txt", "hello")
  assert {:ok, "hello"} = Jido.VFS.read(fs, "test.txt")
end
```