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.
28 lines
1.0 KiB
Rust
28 lines
1.0 KiB
Rust
use crate::cache::create_readonly_conn;
|
|
use crate::commands::shared::{find_file_by_name, resolve_remote_file};
|
|
use crate::error::response_error;
|
|
use anyhow::anyhow;
|
|
|
|
pub async fn file_metadata(remote_path: &str, password: Option<&str>) -> Result<(), anyhow::Error> {
|
|
let remote = remote_path
|
|
.split(':')
|
|
.next()
|
|
.ok_or_else(|| anyhow!("Invalid remote path"))?;
|
|
let (conn, cache) = create_readonly_conn(remote).await?;
|
|
let (parent_folder, basename) =
|
|
resolve_remote_file(&conn, cache.as_ref(), remote_path, password).await?;
|
|
|
|
let files = conn.list_all_files(&parent_folder.id).await?;
|
|
|
|
let file = find_file_by_name(&files, &basename)
|
|
.ok_or_else(|| anyhow!("File `{}` not found.", basename))?;
|
|
|
|
let resp = conn.get(&format!("v3/files/{}/metadata/", file.id)).await?;
|
|
if !resp.status().is_success() {
|
|
return Err(response_error("Failed to get file metadata", resp).await);
|
|
}
|
|
let json: serde_json::Value = resp.json().await?;
|
|
println!("{}", serde_json::to_string(&json)?);
|
|
Ok(())
|
|
}
|