Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 49 additions & 8 deletions crates/cli/src/commands/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ pub async fn execute(args: LsArgs, output_config: OutputConfig) -> ExitCode {
}
};

if bucket.is_none() {
if args.recursive {
// If no bucket specified & recursive flag is provided, list all objects on the server
return list_all_objects(&client, alias_name, &formatter, args.summarize).await;
} else {
// Else no bucket specified, list buckets
return list_buckets(&client, &formatter, args.summarize).await;
}
if let Some(list_mode) = alias_listing_mode(bucket.as_ref(), args.recursive) {
return match list_mode {
AliasListingMode::Buckets => list_buckets(&client, &formatter, args.summarize).await,
// Keep mc-compatible behavior for `ls alias/ -r`.
AliasListingMode::AllObjects => {
list_all_objects(&client, alias_name, &formatter, args.summarize).await
}
};
}

let bucket = bucket.unwrap();
Expand Down Expand Up @@ -390,6 +390,24 @@ async fn list_objects_with_paging(
Ok((all_items, is_truncated, continuation_token))
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AliasListingMode {
Buckets,
AllObjects,
}

fn alias_listing_mode(bucket: Option<&String>, recursive: bool) -> Option<AliasListingMode> {
if bucket.is_some() {
return None;
}

if recursive {
Some(AliasListingMode::AllObjects)
Comment on lines +399 to +405
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

alias_listing_mode takes Option<&String>, which unnecessarily couples the helper to String and forces callers to pass bucket.as_ref(). Consider taking Option<&str> instead (and call with bucket.as_deref()), since the function only needs to know whether a bucket is present and this matches other helpers in the CLI that accept &str/Option<&str>.

Copilot uses AI. Check for mistakes.
} else {
Some(AliasListingMode::Buckets)
}
}

/// Parse ls path into (alias, bucket, prefix)
fn parse_ls_path(path: &str) -> Result<(String, Option<String>, Option<String>), String> {
let path = path.trim_end_matches('/');
Expand Down Expand Up @@ -452,4 +470,27 @@ mod tests {
fn test_parse_ls_path_empty() {
assert!(parse_ls_path("").is_err());
}

#[test]
fn test_alias_listing_mode_lists_all_objects_for_recursive_alias_path() {
assert_eq!(
alias_listing_mode(None, true),
Some(AliasListingMode::AllObjects)
);
}

#[test]
fn test_alias_listing_mode_lists_buckets_without_recursive_flag() {
assert_eq!(
alias_listing_mode(None, false),
Some(AliasListingMode::Buckets)
);
}

#[test]
fn test_alias_listing_mode_ignores_alias_only_logic_when_bucket_is_present() {
let bucket = "demo".to_string();
assert_eq!(alias_listing_mode(Some(&bucket), false), None);
assert_eq!(alias_listing_mode(Some(&bucket), true), None);
}
}
Loading