Skip to main content
ZipDrop exposes several Tauri commands that the frontend can invoke to interact with the Rust backend.

Core Workflow

process_and_upload

Main command that processes files and uploads them to R2 or saves locally in demo mode.
#[tauri::command]
async fn process_and_upload(
    state: tauri::State<'_, AppState>,
    paths: Vec<String>,
) -> Result<DropResult, String>
state
AppState
required
Application state containing R2 config and settings
paths
Vec<String>
required
Array of file paths (as strings) to process and upload
Returns: Result<DropResult, String>

DropResult Structure

url
String
required
Public URL to access the uploaded file. Format: https://your-domain.com/u/{key} for R2 uploads, or file:///path/to/file for demo mode
local_path
Option<String>
Path to the local file (only set in demo mode)
r2_key
Option<String>
R2 object key (only set when uploaded to R2)
original_size
u64
required
Total size of original file(s) in bytes
processed_size
u64
required
Size of processed file in bytes
file_type
String
required
File extension of the output (“webp”, “zip”, etc.)
is_demo
bool
required
Whether the file was processed in demo mode
Workflow:
  1. Checks if demo mode is enabled
  2. Determines output directory (Downloads/ZipDrop for demo, /tmp/zipdrop for production)
  3. Processes files (convert to WebP or ZIP)
  4. Either:
    • Demo mode: Saves to Downloads/ZipDrop, copies local path to clipboard
    • Production mode: Uploads to R2, copies public URL to clipboard
  5. Returns DropResult with all metadata
Example Invocation (JavaScript):
const result = await invoke('process_and_upload', {
  paths: ['/Users/john/Desktop/photo.jpg']
});

// Result:
// {
//   url: "file:///Users/john/Downloads/ZipDrop/photo_a1b2c3d4.webp",
//   local_path: "/Users/john/Downloads/ZipDrop/photo_a1b2c3d4.webp",
//   r2_key: null,
//   original_size: 2500000,
//   processed_size: 850000,
//   file_type: "webp",
//   is_demo: true
// }
Error Cases:
  • Empty paths array: "No files provided"
  • Validation failures: See File Processing errors
  • R2 not configured (production mode): "R2 not configured. Please set up your R2 credentials or enable demo mode."
  • Upload failures: Network or credential errors from R2

Configuration Management

set_r2_config

Saves R2 configuration to macOS Keychain and updates app state.
#[tauri::command]
fn set_r2_config(state: tauri::State<'_, AppState>, config: R2Config) -> Result<(), String>
state
AppState
required
Application state
config
R2Config
required
R2 configuration object with all credentials
R2Config Structure:
access_key
String
required
R2 access key ID
secret_key
String
required
R2 secret access key
bucket_name
String
required
R2 bucket name
account_id
String
required
Cloudflare account ID
public_url_base
String
required
Base URL for public access (e.g., https://files.example.com)
Side Effects:
  • Saves credentials to macOS Keychain (secure storage)
  • Saves non-secret config to ~/Library/Application Support/zipdrop/config.json
  • Automatically disables demo mode
  • Updates in-memory app state
Example Invocation:
await invoke('set_r2_config', {
  config: {
    access_key: 'your-access-key',
    secret_key: 'your-secret-key',
    bucket_name: 'my-bucket',
    account_id: 'abc123def456',
    public_url_base: 'https://files.example.com'
  }
});

get_r2_config

Retrieves the current R2 configuration (for populating settings forms).
#[tauri::command]
fn get_r2_config(state: tauri::State<'_, AppState>) -> Option<R2Config>
Returns: Option<R2Config> - Configuration if set, null if not configured Example:
const config = await invoke('get_r2_config');
if (config) {
  // Populate form with config.bucket_name, etc.
}

get_config_status

Returns the current configuration status (lighter weight than get_r2_config).
#[tauri::command]
fn get_config_status(state: tauri::State<'_, AppState>) -> ConfigStatus
ConfigStatus Structure:
is_configured
bool
required
Whether R2 credentials are configured
demo_mode
bool
required
Whether demo mode is enabled
bucket_name
Option<String>
Current bucket name if configured
Example:
const status = await invoke('get_config_status');
// { is_configured: true, demo_mode: false, bucket_name: "my-bucket" }

clear_r2_config

Deletes R2 configuration from Keychain and file system.
#[tauri::command]
fn clear_r2_config(state: tauri::State<'_, AppState>) -> Result<(), String>
Side Effects:
  • Removes credentials from macOS Keychain
  • Deletes ~/Library/Application Support/zipdrop/config.json
  • Clears in-memory app state
Example:
await invoke('clear_r2_config');

validate_r2_config

Validates R2 credentials before saving by uploading a test object.
#[tauri::command]
async fn validate_r2_config(config: R2Config) -> Result<(), String>
config
R2Config
required
R2 configuration to validate
Validation Process:
  1. Creates S3/R2 client with provided credentials
  2. Uploads tiny test object (.zipdrop-connection-test)
  3. Deletes test object if upload succeeds
  4. Returns error if any step fails
Error Messages:
  • "Connection timed out - please try again" - Network timeout
  • "Connection failed - check your network" - Network unreachable
  • "Invalid R2 credentials" - Auth failure or other error
Example:
try {
  await invoke('validate_r2_config', { config });
  // Credentials are valid, proceed with set_r2_config
} catch (error) {
  // Show error to user
}

Settings

set_demo_mode

Enables or disables demo mode.
#[tauri::command]
fn set_demo_mode(state: tauri::State<'_, AppState>, enabled: bool) -> Result<(), String>
enabled
bool
required
Whether to enable demo mode
Side Effects:
  • Updates ~/Library/Application Support/zipdrop/settings.json
  • Updates in-memory app state
Example:
await invoke('set_demo_mode', { enabled: true });

Utility Commands

copy_to_clipboard

Copies text to the system clipboard.
#[tauri::command]
fn copy_to_clipboard(text: String) -> Result<(), String>
text
String
required
Text to copy to clipboard
Example:
await invoke('copy_to_clipboard', { 
  text: 'https://files.example.com/u/file.zip' 
});

reveal_in_finder

Opens Finder and selects the specified file.
#[tauri::command]
fn reveal_in_finder(path: String) -> Result<(), String>
path
String
required
Absolute path to the file to reveal
Example:
await invoke('reveal_in_finder', { 
  path: '/Users/john/Downloads/ZipDrop/file.webp' 
});

open_in_browser

Opens a URL in the default browser.
#[tauri::command]
fn open_in_browser(url: String) -> Result<(), String>
url
String
required
URL to open
Example:
await invoke('open_in_browser', { 
  url: 'https://files.example.com/u/file.zip' 
});

delete_from_r2

Deletes an object from R2 storage.
#[tauri::command]
async fn delete_from_r2(state: tauri::State<'_, AppState>, key: String) -> Result<(), String>
state
AppState
required
Application state containing R2 config
key
String
required
R2 object key to delete (e.g., "u/x7y8z9w0_file.zip")
Example:
await invoke('delete_from_r2', { 
  key: 'u/x7y8z9w0_file.zip' 
});
This command is fire-and-forget. Errors are logged but the operation continues. Use for cleanup when you don’t need to guarantee deletion.

App State

All commands that require state access the global AppState structure:
pub struct AppState {
    pub r2_config: Mutex<Option<R2Config>>,
    pub settings: Mutex<AppSettings>,
}
r2_config
Mutex<Option<R2Config>>
required
Thread-safe R2 configuration (None if not configured)
settings
Mutex<AppSettings>
required
Thread-safe app settings (demo mode, output directory)
State is managed by Tauri and automatically injected into command handlers. Frontend code doesn’t need to pass state explicitly.

Build docs developers (and LLMs) love