Expand description
§app-path
Create file paths relative to your executable for truly portable applications.
This crate provides a simple, robust solution for applications that need to access files and directories relative to their executable location. The design prioritizes simplicity, performance, and reliability for the common use case of portable applications.
§Design Philosophy
AppPath is designed around these core principles:
- Portable-first: Everything stays together with your executable
- Simple API: Infallible constructors with clear panic conditions
- High performance: Static caching and zero-allocation design
- Ergonomic: Works seamlessly with all Rust path types
- Robust: Handles edge cases in containerized/embedded environments
The crate makes a deliberate trade-off: simplicity and performance over fallible APIs. Since executable location determination rarely fails in practice, and when it does fail it indicates fundamental system issues, we choose to panic with clear documentation rather than burden every usage site with error handling.
§Primary Use Cases
This crate is specifically designed for applications that benefit from portable layouts:
- Portable applications that run from USB drives or network shares
- Development tools that should work without installation
- Corporate software deployed without admin rights
- Containerized applications with predictable file layouts
- Embedded systems with simple, fixed directory structures
- CLI tools that need configuration and data files nearby
§Quick Start
use app_path::AppPath;
use std::path::{Path, PathBuf};
// Create paths relative to your executable - simple and clean
let config = AppPath::new("config.toml");
let data = AppPath::new("data/users.db");
// Accepts any AsRef<Path> type - no unnecessary allocations
let log_file = "logs/app.log".to_string();
let logs = AppPath::new(&log_file);
let path_buf = PathBuf::from("cache/data.bin");
let cache = AppPath::new(&path_buf);
// Works with any path-like type
let from_path = AppPath::new(Path::new("temp.txt"));
// Alternative: Use From for any path type
let settings: AppPath = "settings.json".into();
let data_file: AppPath = PathBuf::from("data.db").into();
let temp_file: AppPath = Path::new("temp.log").into();
// Get the paths for use with standard library functions
println!("Config: {}", config.path().display());
println!("Data: {}", data.path().display());
// Check existence and create directories
if !logs.exists() {
logs.create_dir_all()?;
}
§Path Resolution Strategy
AppPath uses intelligent path resolution to support both portable and system-integrated applications:
use app_path::AppPath;
// Relative paths are resolved relative to the executable directory
// This is the primary use case for portable applications
let config = AppPath::new("config.toml"); // → exe_dir/config.toml
let data = AppPath::new("data/users.db"); // → exe_dir/data/users.db
let nested = AppPath::new("plugins/my_plugin.dll"); // → exe_dir/plugins/my_plugin.dll
// Absolute paths are used as-is for system integration
// This allows hybrid applications that also integrate with the system
let system_config = AppPath::new("/etc/myapp/config.toml"); // → /etc/myapp/config.toml
let windows_temp = AppPath::new(r"C:\temp\cache.dat"); // → C:\temp\cache.dat
This dual behavior enables applications to be primarily portable while still allowing system integration when needed.
§Performance and Memory Design
AppPath is optimized for high-performance applications:
- Static caching: Executable directory determined once, cached forever
- Minimal memory: Only stores the final resolved path (no input path retained)
- Zero allocations: Uses
AsRef<Path>
to avoid unnecessary conversions - Efficient conversions:
From
trait implementations for all common types - Thread-safe: Safe concurrent access to cached executable directory
use app_path::AppPath;
use std::path::{Path, PathBuf};
// All of these are efficient - no unnecessary allocations
let from_str = AppPath::new("config.toml"); // &str → direct usage
let from_string = AppPath::new(&"data.db".to_string()); // &String → no move needed
let from_path = AppPath::new(Path::new("logs.txt")); // &Path → direct usage
let from_pathbuf = AppPath::new(&PathBuf::from("cache.bin")); // &PathBuf → no move needed
// When you want ownership transfer, use From trait
let owned: AppPath = PathBuf::from("important.db").into(); // PathBuf moved efficiently
§Reliability and Edge Cases
AppPath is designed to be robust in various deployment environments:
- Handles root-level executables: Works when executable is at filesystem root
- Container-friendly: Designed for containerized and jailed environments
- Cross-platform: Consistent behavior across Windows, Linux, and macOS
- Clear failure modes: Panics with descriptive messages on rare system failures
§Panic Conditions
The crate uses an infallible API that panics on rare system failures during static initialization. This design choice prioritizes simplicity and performance for the common case where executable location determination succeeds (which is the vast majority of real-world usage).
AppPath will panic if:
- Cannot determine executable location - When
std::env::current_exe()
fails (rare, but possible in some embedded or heavily sandboxed environments) - Executable path is empty - When the system returns an empty executable path (extremely rare, indicates a broken/corrupted system)
These panics occur:
- Once during the first use of any AppPath function
- Indicate fundamental system issues that are typically unrecoverable
- Are documented with clear error messages explaining the failure
Edge case handling:
- Root-level executables: When executable runs at filesystem root (e.g.,
/init
,C:\
), the crate uses the root directory itself as the base - Containerized environments: Properly handles Docker, chroot, and other containerized environments
- Jailed environments: Handles various forms of process isolation and sandboxing
For applications that need fallible behavior, consider these practical alternatives:
use app_path::AppPath;
use std::env;
// Pattern 1: Environment variable fallback (recommended)
fn get_config_path() -> AppPath {
if let Ok(custom_dir) = env::var("MYAPP_CONFIG_DIR") {
let config_path = std::path::Path::new(&custom_dir).join("config.toml");
AppPath::new(config_path)
} else {
AppPath::new("config.toml")
}
}
// Pattern 2: Conditional development/production paths
fn get_data_path() -> AppPath {
if env::var("DEVELOPMENT").is_ok() {
let dev_path = env::current_dir().unwrap().join("dev_data").join("app.db");
AppPath::new(dev_path)
} else {
AppPath::new("data/app.db")
}
}
For applications that need to handle executable location failures:
use app_path::AppPath;
use std::env;
fn get_config_path_safe() -> AppPath {
match env::current_exe() {
Ok(exe_path) => {
if let Some(exe_dir) = exe_path.parent() {
let config_path = exe_dir.join("config.toml");
AppPath::new(config_path)
} else {
// Fallback for edge case where exe has no parent
let temp_dir = env::temp_dir().join("myapp");
let _ = std::fs::create_dir_all(&temp_dir);
let config_path = temp_dir.join("config.toml");
AppPath::new(config_path)
}
}
Err(_) => {
// Fallback when executable location cannot be determined
let temp_dir = env::temp_dir().join("myapp");
let _ = std::fs::create_dir_all(&temp_dir);
let config_path = temp_dir.join("config.toml");
AppPath::new(config_path)
}
}
}
Note: Using std::env::current_exe()
directly is simpler and more idiomatic
than panic::catch_unwind
patterns. Most applications should use environment
variable and conditional patterns instead.
§Flexible Creation Methods
use app_path::AppPath;
use std::path::{Path, PathBuf};
// Method 1: Direct construction (recommended)
let config = AppPath::new("config.toml");
let logs = AppPath::new(PathBuf::from("logs/app.log"));
// Method 2: From trait for various path types
let data1: AppPath = "data/users.db".into(); // &str
let data2: AppPath = "settings.json".to_string().into(); // String
let data3: AppPath = Path::new("cache/data.bin").into(); // &Path
let data4: AppPath = PathBuf::from("temp/file.txt").into(); // PathBuf
// All methods support efficient zero-copy when possible
let owned_path = PathBuf::from("important/data.db");
let app_path: AppPath = owned_path.into(); // PathBuf is moved efficiently
Structs§
- AppPath
- Creates paths relative to the executable location for applications.
Functions§
- exe_dir
- Get the executable’s directory.