A review against the Python client, which shares this client's config
and login cache, found the transfers holding whole files in memory and
several commands answering differently from their Python counterparts.
- Stream uploads and downloads instead of buffering the whole file, so
memory no longer scales with file size times concurrency. Uploads
still declare a Content-Length rather than going out chunked.
- Write a download beside its destination and move it into place once
complete, and refuse a destination that cannot be written, so a
failed transfer leaves what was already there untouched.
- Report the server's own error detail instead of the bare status.
- List locked sub-folders in `ls --json --recursive` using the given
password and skip only those that cannot be unlocked, instead of
failing the whole listing.
- Name each folder's laboratory in JSON output and sort its entries,
matching the Python client's schema and order.
- Ask the server who is logged in for `whoami`, rather than trusting
the cached name.
- Collapse repeated separators and refuse `..` in remote paths.
- Fall back to the API when a laboratory is missing from the cache, so
a newly added one no longer needs a fresh login.
- Verify the login cache digest on read, as the Python client does.
- Accept `-e` as the short form of `--exclude`.
- Satisfy clippy and rustfmt across the crate.
Move token refresh checks into the shared Rust connection/API path so long-running authenticated operations stop reusing stale access tokens. This covers recursive download and upload traversal, recursive ls via the shared APIs, and direct authenticated commands such as cp, mv, rm, and chacl.
Also surface HTTP failures earlier in the affected API methods instead of failing later during response parsing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 5: Replace all Box<dyn Error> return types with anyhow::Result<T>
throughout the codebase. Replace string-based Err("msg".into()) and
format!().into() patterns with bail!() and anyhow!() macros. Fix
dirs::home_dir().unwrap() in settings.rs to use a fallback path instead
of panicking when HOME is unset. Remove stray use std::error::Error
imports no longer needed.
Phase 6: Add From<&User> for CacheUser in models/user.rs and
From<&Laboratory>/From<&Laboratories> for CacheLaboratory/CacheLabsWrapper
in models/laboratory.rs. Simplify commands/login.rs to use .into()
conversions, removing the redundant to_cache_user() and to_cache_labs()
helper functions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- api/files.rs: NFC-normalize filename before sending to server in
upload_file(). On macOS, local filenames may be NFD-encoded, which
would cause the server to store them as NFD instead of NFC.
- commands/download.rs: replace direct to_lowercase() subfolder
comparison with find_subfolder_by_name() helper, which already
applies NFC normalization on both sides.
- commands/cp.rs, mv.rs: apply nfc() to s_basename (source path
component from user input) for consistency with d_basename, so
the no-op identity check and find_*() calls use normalized strings.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>