logical-path 0.1.0

Translate canonical (symlink-resolved) filesystem paths back to their logical (symlink-preserving) equivalents
Documentation
# Examples

Real-world usage patterns for the `logical-path` crate.

## Shell Integration (cd Directives)

The most common use case: a Rust CLI tool that emits `cd` directives for shell integration (e.g., a directory jumper, a project switcher, or a git worktree tool).

Without `logical-path`, the emitted path resolves all symlinks, moving the user out of their logical directory tree:

```rust
use logical_path::LogicalPathContext;
use std::path::Path;

fn emit_cd_directive(target: &Path) {
    let ctx = LogicalPathContext::detect();

    // Translate the canonical path to preserve the user's symlink structure.
    let display_path = ctx.to_logical(target);

    // Emit a cd directive that keeps the user in their logical directory.
    println!("cd {}", display_path.display());
}
```

## Displaying Paths to Users

Any tool that shows filesystem paths in its output (status bars, error messages, file listings) should translate canonical paths to logical paths for a consistent user experience:

```rust
use logical_path::LogicalPathContext;
use std::path::Path;

struct App {
    ctx: LogicalPathContext,
}

impl App {
    fn new() -> Self {
        App {
            ctx: LogicalPathContext::detect(),
        }
    }

    fn display_file(&self, canonical_path: &Path) {
        let display = self.ctx.to_logical(canonical_path);
        println!("  {}", display.display());
    }
}
```

## Git Worktree Path Comparison

`git worktree list` returns canonical paths. If your tool compares worktree paths against the user's `$PWD`, the paths won't match without translation:

```rust
use logical_path::LogicalPathContext;
use std::path::{Path, PathBuf};

fn find_current_worktree(worktrees: &[PathBuf], current_dir: &Path) -> Option<PathBuf> {
    let ctx = LogicalPathContext::detect();

    // Translate each worktree path to logical form for comparison
    // against the user's perceived current directory.
    for worktree in worktrees {
        let logical = ctx.to_logical(worktree);
        if current_dir.starts_with(&logical) {
            return Some(logical);
        }
    }
    None
}
```

## Global Context with `OnceLock`

For applications that need the context in multiple places, initialize it once and share it globally:

```rust
use logical_path::LogicalPathContext;
use std::sync::OnceLock;

static PATH_CTX: OnceLock<LogicalPathContext> = OnceLock::new();

fn path_ctx() -> &'static LogicalPathContext {
    PATH_CTX.get_or_init(LogicalPathContext::detect)
}
```

## Translating Paths from External Tools

When receiving paths from external tools (e.g., `git`, `cargo`, LSP servers), those paths are typically canonical. Translate them before displaying to the user:

```rust
use logical_path::LogicalPathContext;
use std::path::PathBuf;

fn process_tool_output(canonical_paths: Vec<PathBuf>) -> Vec<PathBuf> {
    let ctx = LogicalPathContext::detect();
    canonical_paths
        .iter()
        .map(|p| ctx.to_logical(p))
        .collect()
}
```

## Normalizing User Input

When the user provides a logical path that needs to be passed to a filesystem API, translate it to canonical form:

```rust
use logical_path::LogicalPathContext;
use std::path::Path;

fn open_file(user_path: &Path) -> std::io::Result<String> {
    let ctx = LogicalPathContext::detect();

    // Translate to canonical for filesystem operations.
    let fs_path = ctx.to_canonical(user_path);

    std::fs::read_to_string(&fs_path)
}
```

## Diagnostic Output

Use `has_mapping()` and `Debug` formatting for diagnostics:

```rust
use logical_path::LogicalPathContext;

fn print_diagnostics() {
    let ctx = LogicalPathContext::detect();

    if ctx.has_mapping() {
        println!("Path indirection prefix mapping detected:");
        println!("  Context: {:?}", ctx);
    } else {
        println!("No prefix mapping detected.");
        println!("  Logical and canonical CWD are consistent (or CWD cannot be determined).");
    }
}
```

## Windows: Junction-Aware Path Display

On Windows, junctions and subst drives are detected automatically. The same API works across all platforms:

```rust
use logical_path::LogicalPathContext;
use std::path::Path;

fn display_project_path(canonical_path: &Path) {
    let ctx = LogicalPathContext::detect();

    // On Windows with junction C:\workspace → D:\projects\workspace:
    //   canonical_path = D:\projects\workspace\src\main.rs
    //   logical_path   = C:\workspace\src\main.rs
    //
    // On Unix with symlink /workspace → /mnt/wsl/workspace:
    //   canonical_path = /mnt/wsl/workspace/src/main.rs
    //   logical_path   = /workspace/src/main.rs
    //
    // On any platform with no indirections:
    //   logical_path   = canonical_path (unchanged)
    let logical_path = ctx.to_logical(canonical_path);
    println!("  {}", logical_path.display());
}
```

## Integration with `clap` CLI Applications

A typical pattern for a `clap`-based CLI tool:

```rust
use logical_path::LogicalPathContext;
use std::path::PathBuf;

struct Cli {
    target: PathBuf,
}

fn run(cli: Cli) {
    let ctx = LogicalPathContext::detect();

    // Translate the target path for display.
    let display_path = ctx.to_logical(&cli.target);
    println!("Working in: {}", display_path.display());

    // Use the original (canonical) path for filesystem operations.
    // Or use to_canonical() if the input was a logical path from the user.
}
```