documentation/cheatsheet.cheatmd

# Reactor.File Cheatsheet

Reactor.File is an extension that provides file system operations as steps within Reactor workflows.

## Getting Started
{: .col-2}

### Basic Setup
```elixir
defmodule MyFileReactor do
  use Reactor, extensions: [Reactor.File]

  input :source_path
  input :dest_path

  read_file :content do
    path input(:source_path)
  end

  write_file :output do
    path input(:dest_path)
    content result(:content)
  end

  return :output
end
```

### Installation
```elixir
def deps do
  [
    {:reactor_file, "~> 0.18.2"}
  ]
end
```

## File Operations
{: .col-2}

### Reading Files
```elixir
# Read entire file
read_file :config do
  path value("/etc/config.json")
end

# Using inputs/results
read_file :user_data do
  path input(:file_path)
end
```

### Writing Files
```elixir
# Write simple content
write_file :output do
  path input(:output_path)
  content value("Hello, World!")
end

# Write with revert capability
write_file :backup do
  path input(:backup_path)
  content result(:processed_data)
  revert_on_undo? true
end
```

### Copying Files
```elixir
# Copy single file
cp :backup_file do
  source input(:source_path)
  destination input(:dest_path)
end

# Copy recursively
cp_r :backup_directory do
  source input(:source_dir)
  destination input(:backup_dir)
end
```

## Directory Operations
{: .col-2}

### Creating Directories
```elixir
# Create single directory
mkdir :new_dir do
  path input(:new_directory_path)
end

# Create with parents
mkdir_p :deep_path do
  path input(:deep_directory_path)
  revert_on_undo? true
end
```

### File Discovery
```elixir
# Find files by pattern
glob :txt_files do
  pattern input(:search_pattern)
end

# Include hidden files
glob :all_configs do
  pattern value("/etc/**/*.conf")
  match_dot true
end
```

### Removing Files
```elixir
# Remove single file
rm :temp_file do
  path input(:file_to_remove)
end

# Remove directory
rmdir :empty_dir do
  path input(:directory_to_remove)
end
```

## File I/O Operations
{: .col-3}

### File Handles
```elixir
# Open file for reading
open_file :file_handle do
  path input(:data_file_path)
  modes [:read]
end

# Open for writing
open_file :output_handle do
  path input(:log_file_path)
  modes [:write, :append]
end

# Close when done
close_file :cleanup do
  file result(:file_handle)
end
```

### Stream Reading
```elixir
# Read as lines
io_stream :lines do
  file result(:file_handle)
end

# Binary streaming
io_binstream :chunks do
  file result(:binary_handle)
  line_or_bytes 4096
end
```

### Direct I/O
```elixir
# Read specific amount
io_read :chunk do
  file result(:file_handle)
  length 1024
end

# Write to file handle
io_write :append_data do
  file result(:output_handle)
  data input(:log_message)
end
```

## File Metadata
{: .col-2}

### File Information
```elixir
# Get file stats
stat :file_info do
  path input(:target_file)
end

# Get link stats (doesn't follow symlinks)
lstat :link_info do
  path input(:symlink_path)
end

# Read symlink target
read_link :target do
  path input(:symlink_path)
end
```

### Creating Links
```elixir
# Hard link
ln :hard_link do
  existing input(:original_file)
  new input(:hardlink_path)
end

# Symbolic link
ln_s :soft_link do
  existing input(:original_file)
  new input(:symlink_path)
end
```

## Permissions & Ownership
{: .col-2}

### Changing Permissions
```elixir
# Set file permissions
chmod :make_executable do
  path input(:script_path)
  mode value(0o755)
end

# Use template for dynamic permissions
chmod :set_perms do
  path input(:file_path)
  mode input(:permission_mode)
  revert_on_undo? true
end
```

### Changing Ownership
```elixir
# Change owner
chown :change_owner do
  path input(:log_file)
  uid input(:new_owner_id)
  revert_on_undo? true
end

# Change group
chgrp :change_group do
  path input(:shared_directory)
  gid input(:group_id)
end
```

### Writing File Stats
```elixir
# Update timestamps and permissions
write_stat :update_metadata do
  path input(:target_file)
  stat result(:new_stat_info)
end
```

## Advanced Patterns
{: .col-2}

### File Processing Pipeline
```elixir
defmodule ProcessFiles do
  use Reactor, extensions: [Reactor.File]

  input :directory

  glob :source_files do
    pattern input(:directory), transform: &Path.join(&1, "*.txt")
  end

  map :process_files do
    source result(:source_files)

    read_file :content do
      path element(:process_files)
    end

    step :transform do
      argument :data, result(:content)
      run &String.upcase/1
    end

    write_file :output do
      path element(:process_files), transform: &(&1 <> ".processed")
      content result(:transform)
    end

    return :output
  end

  return :process_files
end
```

### Backup and Restore
```elixir
# Create backup with metadata preservation
stat :original_stats do
  path input(:source_file)
end

cp :backup_file do
  source input(:source_file)
  destination input(:backup_path)
end

write_stat :preserve_metadata do
  path input(:backup_path)
  stat result(:original_stats)
end
```

### File Validation
```elixir
# Check file exists and is readable
stat :validate_source do
  path input(:source_path)
end

step :check_permissions do
  argument :stat, result(:validate_source)
  run fn %{stat: %{access: access}}, _context ->
    if :read in access do
      {:ok, :valid}
    else
      {:error, "File not readable"}
    end
  end
end
```

## Error Handling
{: .col-2}

### Common Patterns
```elixir
# Handle missing files gracefully
read_file :optional_config do
  path value("/etc/app/config.json")
end

step :use_defaults do
  argument :config, result(:optional_config)
  run fn
    %{config: content}, _context -> 
      {:ok, Jason.decode!(content)}
    %{}, _context -> 
      {:ok, %{default: true}}
  end
  where config_exists?/1
end

# Guard function
def config_exists?(%{config: _}), do: true
def config_exists?(_), do: false
```

### Undoable Operations
```elixir
# Operations that can be reverted
mkdir_p :temp_workspace do
  path input(:workspace_path)
  revert_on_undo? true
end

write_file :temp_data do
  path input(:workspace_path), transform: &Path.join(&1, "data.json")
  content input(:json_data)
  revert_on_undo? true
end

chmod :secure_temp do
  path input(:workspace_path)
  mode value(0o700)
  revert_on_undo? true
end
```

## Common Options
{: .col-2}

### Universal Options

| Option | Type | Description |
| --- | --- | --- |
| `name` | `atom` | Unique step identifier |
| `description` | `string` | Optional step description |

### Revert Options

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `revert_on_undo?` | `boolean` | `false` | Restore original state on undo |

### File Mode Options

| Mode | Description |
| --- | --- |
| `:read` | Read-only access |
| `:write` | Write access |
| `:append` | Append to file |
| `:exclusive` | Fail if file exists |
| `:raw` | Raw binary mode |
| `:binary` | Binary mode |
| `:compressed` | Enable compression |