Virtual File System
View SourceAn in-memory filesystem that's purely functional: immutable struct, O(1) path lookups, zero side effects. Good for sandboxed environments and poking around in the REPL.
Core API
alias Raxol.Commands.FileSystem
fs = FileSystem.new()
{:ok, fs} = FileSystem.mkdir(fs, "/docs")
{:ok, fs} = FileSystem.create_file(fs, "/docs/readme.txt", "Hello!")
{:ok, entries} = FileSystem.ls(fs, "/docs") # => ["readme.txt"]
{:ok, content} = FileSystem.cat(fs, "/docs/readme.txt") # => "Hello!"
{:ok, fs} = FileSystem.cd(fs, "/docs")
FileSystem.pwd(fs) # => "/docs"
FileSystem.exists?(fs, "readme.txt") # => true
{:ok, info} = FileSystem.stat(fs, "readme.txt")
# => %{type: :file, size: 6, path: "/docs/readme.txt", ...}
{:ok, fs} = FileSystem.rm(fs, "readme.txt")
{:ok, tree} = FileSystem.tree(fs, "/", 3)
# => {"/", :directory, [{"docs", :directory, []}]}Anything that changes the filesystem returns {:ok, new_fs} or {:error, reason}. Error reasons are atoms: :not_found, :already_exists, :parent_not_found, :not_a_directory, :is_a_directory, :directory_not_empty, :cannot_remove_root. Every non-root node has a directory parent; mkdir/2 and create_file/3 return {:error, :not_a_directory} without changing the filesystem when the parent path is a file.
REPL Integration
Call Evaluator.with_vfs/1 to get a vfs binding and shell-like helpers auto-imported from Raxol.REPL.VfsHelpers:
alias Raxol.REPL.Evaluator
eval = Evaluator.new() |> Evaluator.with_vfs()
{:ok, _, eval} = Evaluator.eval(eval, "vfs = mkdir(vfs, \"/src\")")
{:ok, _, eval} = Evaluator.eval(eval, "vfs = touch(vfs, \"/src/app.ex\", \"defmodule App do\\nend\")")
{:ok, _, eval} = Evaluator.eval(eval, "vfs = ls(vfs)")
{:ok, _, eval} = Evaluator.eval(eval, "vfs = cat(vfs, \"/src/app.ex\")")
{:ok, _, eval} = Evaluator.eval(eval, "tree(vfs)")Helpers print their output via IO (captured by the evaluator) and return the VFS struct so you can chain them.
| Helper | What it does | Mutates VFS? |
|---|---|---|
ls(vfs) / ls(vfs, path) | Print directory listing | No |
cd(vfs, path) | Change working directory | Yes |
pwd(vfs) | Print current directory | No |
cat(vfs, path) | Print file contents | No |
mkdir(vfs, path) | Create directory | Yes |
touch(vfs, path) / touch(vfs, path, content) | Create file | Yes |
rm(vfs, path) | Remove file or empty dir | Yes |
tree(vfs) / tree(vfs, path, depth) | Print directory tree | No |
stat(vfs, path) | Print node metadata | No |
The Evaluator.prelude field imports VfsHelpers before every eval, which is why bare names like ls and mkdir work without any aliasing.
Internals
Internally it's a flat map keyed by absolute path (%{"/" => node, "/docs" => node, ...}). Parent-child relationships are tracked both ways (parents keep a children list). Path resolution handles ., .., absolute paths, relative paths, and - for the previous directory. Timestamps come from System.monotonic_time(:millisecond).
There are also formatting helpers: format_ls/3 for styled directory listings and format_cat/3 for line-numbered file output.
Playground demo
mix raxol.playground has a VFS demo with a shell-like interface: ls, cd, cat, pwd, mkdir, rm, tree, and help all work.