path-lib 1.0.0

Cross-platform application and OS directory resolution for Rust, with user and global scopes, hardened sub-path joining, and guarded filesystem operations. Zero dependencies.
docs.rs failed to build path-lib-1.0.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

Features

Application Directories

  • Eight kinds — data, config, cache, logs, state, runtime, temp, documents
  • Three scopes — App::new (this user), App::global (the machine), App::portable (one folder you choose)
  • Portable detection — App::auto switches to a portable layout when a .portable file sits next to the executable
  • Overrides — with_dir relocates one kind; with_env("HIVEDB") reads HIVEDB_DATA_DIR, HIVEDB_LOGS_DIR, and so on
  • Native conventions — XDG and FHS on Linux and BSD, the Library domains on macOS, Known Folders on Windows
  • Distinct by construction — no two kinds share a directory, so clearing the cache can never wipe data; overrides are checked too

Hardened Sub-Paths

  • Either slash style — "a/b", "a\\b", "/a//b/" all mean the same thing and produce the platform separator
  • Reject, don't repair — traversal, absolute paths, reserved names and characters fail before any path is built
  • sanitize — lossy repair for user-supplied names; its output is always accepted

OS Folders

  • home, documents, desktop, downloads, pictures, music, videos — redirect-aware
  • root, temp, exe, exe_dir — plus os(), a compile-time constant
  • expand_home — ~ and ~/rest for paths read from configuration; $VARS are deliberately left alone
  • Resolved once — cached for the life of the process; later calls cost about a nanosecond

Guarded File Operations

  • make_dir, make_file, exists, list, remove, copy, move_to, find
  • Never overwrites, never follows links, refuses catastrophic deletes
  • Atomic copies — staged under a temporary name, renamed into place when complete
  • write_atomic — replaces a file through a flushed temporary and a rename; keeps Unix permissions and symbolic links

Installation

[dependencies]
path-lib = "1.0"

Quick Start

use path_lib::{App, Kind};

// Per-user directories, relocatable with HIVEDB_DATA_DIR and friends.
let app = App::new("HiveDB")?.with_env("HIVEDB")?;

// Base directories, resolved once.
println!("{}", app.data().display());     // ~/Library/Application Support/HiveDB on macOS

// Hardened sub-paths: any slash style, always inside the base.
let db = app.data_at("/big\\sloppy//floppy/")?;
assert!(app.data_at("../../etc/passwd").is_err());

// Create on demand (0700 on Unix for user scope).
let wal = app.create(Kind::Data, "db/wal")?;

// Replace a file atomically: readers see the old or the new contents.
path_lib::write_atomic(app.config_at("hivedb.toml")?, "port = 7000\n")?;

// Machine-wide, for services.
let svc = App::global("HiveDB")?;
println!("{}", svc.config().display());   // /etc/HiveDB on Linux
# Ok::<(), path_lib::Error>(())

Where Things Go

Kind Linux (user) macOS (user) Windows (user)
data ~/.local/share/<app> ~/Library/Application Support/<app> %LOCALAPPDATA%\<app>
config ~/.config/<app> ~/Library/Preferences/<app> %APPDATA%\<app>
cache ~/.cache/<app> ~/Library/Caches/<app> %LOCALAPPDATA%\<app>\cache
logs ~/.local/state/<app>/logs ~/Library/Logs/<app> %LOCALAPPDATA%\<app>\logs
Kind Linux (global) macOS (global) Windows (global)
data /var/lib/<app> /Library/Application Support/<app> %ProgramData%\<app>
config /etc/<app> /Library/Preferences/<app> %ProgramData%\<app>\config
cache /var/cache/<app> /Library/Caches/<app> %ProgramData%\<app>\cache
logs /var/log/<app> /Library/Logs/<app> %ProgramData%\<app>\logs

The full tables, including state, runtime, temp, and documents, are in the API reference.

User Input

let app = path_lib::App::new("HiveDB")?;

// Reject: for paths your code builds.
let p = app.documents_at("exports/2026")?;

// Repair: for names a user typed.
let name = path_lib::sanitize("Q3: final?.pdf");   // "Q3_ final_.pdf"
let p = app.documents_at(&name)?;
# Ok::<(), path_lib::Error>(())

Documentation

Contributing

Contributions are welcome under the project's dual license. Before opening a pull request, please make sure:

  1. cargo fmt --all -- --check passes.
  2. cargo clippy --all-targets --all-features -- -D warnings is clean.
  3. cargo test --all-features passes.
  4. New public items include documentation and at least one example.
  5. The API reference and CHANGELOG are updated alongside code changes.