Skip to content

feat: dynamic paths — date, hostname, user and env placeholders - #5

Merged
KiryuuLight merged 2 commits into
devfrom
feat/dynamic-paths
Jul 27, 2026
Merged

KiryuuLight merged 2 commits into
devfrom
feat/dynamic-paths

Conversation

@KiryuuLight

Copy link
Copy Markdown
Contributor

Source and destination paths can now contain placeholders, resolved on every run:

  • Placeholders — {now} / {now:FORMAT} (any strftime format), {utcnow}, {hostname}, {user}, plus $VAR / ${VAR} environment variables. Expanded internally; no shell is involved, so profile fields never execute anything. {{ and $$ are literal escapes.
  • A literal % in a path is untouched — the delimited {…} syntax means an existing folder like 100%done keeps its name.
  • Unknown placeholders and unset variables stay literal, so a typo like {nowx} is visible in the preview instead of silently resolving to the wrong destination.
  • Nested destinations now work — rsync only creates the final directory, so a path like {hostname}/{now:%Y-%m-%d} previously failed at run time with mkdir … No such file or directory even though the dry-run preview reported success. prepare_dest now creates missing parents for local sync destinations, as it already did for snapshots.

Related to #1. Remote destinations with 2+ new levels are still unsupported (parents can't be created on the far side).

@KiryuuLight
KiryuuLight changed the base branch from main to dev July 27, 2026 18:34
@KiryuuLight
KiryuuLight requested a review from Copilot July 27, 2026 18:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “dynamic paths” support so saved rsync profiles can include date/host/user placeholders and environment variables that are expanded internally on each run (no shell). This also improves reliability for nested local sync destinations by creating missing parent directories before invoking rsync.

Changes:

  • Implement placeholder ({now}, {now:...}, {utcnow}, {hostname}, {user}) and environment variable ($VAR, ${VAR}) expansion via crate::paths::expand_path.
  • Ensure nested local sync destinations work by creating destination parent directories in prepare_dest (not just snapshots).
  • Add unit/integration tests plus README documentation for dynamic paths.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/ui/fields.rs Uses expand_path for status checks and updates dest hinting; adds tests for placeholder paths and nested parents.
src/rsync.rs Switches local expansion to expand_path and extends prepare_dest to create parents for sync; adds tests including a live rsync nested-dest case.
src/paths.rs Introduces placeholder + env expansion pipeline (expand_path) and associated tests.
README.md Documents the new dynamic path placeholder/env syntax.
Comments suppressed due to low confidence (1)

src/paths.rs:171

  • path_hits currently treats any non-absolute buffer containing ':' as remote. With formatted placeholders ({now:...}), the literal ':' in the placeholder syntax can incorrectly disable local path completion for inputs like ~/{now:%Y-%m-%d}/. Using the expanded path for the remote-path heuristic avoids this false positive.
pub fn path_hits(buffer: &str) -> Vec<String> {
    if buffer.contains('@') || (buffer.contains(':') && !buffer.starts_with('/')) {
        return Vec::new();
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ui/fields.rs
Comment on lines 104 to 107
pub(crate) fn field_status(buffer: &str, focused: bool, is_dest: bool) -> (Color, Option<String>) {
let b = buffer.trim();
if b.is_empty() || is_remote_buf(b) {
return (accent(), None);
Comment thread README.md Outdated
Comment on lines +180 to +182
Unknown placeholders, unset variables and a bare `%` are left exactly as typed,
and the dry-run preview always shows the resolved path before anything runs.
Write `{{` for a literal brace and `$$` for a literal `$`.
@KiryuuLight
KiryuuLight merged commit 873eb29 into dev Jul 27, 2026
1 check passed
@KiryuuLight
KiryuuLight deleted the feat/dynamic-paths branch July 30, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants