From 17ed2ae5f77cbf10560d4d7d343b796eda616b35 Mon Sep 17 00:00:00 2001 From: overtrue Date: Tue, 14 Apr 2026 22:04:54 +0800 Subject: [PATCH] fix(formatter): classify unsupported feature errors --- crates/cli/src/output/formatter.rs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/output/formatter.rs b/crates/cli/src/output/formatter.rs index a10bbfc..6398a48 100644 --- a/crates/cli/src/output/formatter.rs +++ b/crates/cli/src/output/formatter.rs @@ -148,7 +148,10 @@ fn infer_error_metadata(message: &str) -> (&'static str, bool, Option) { return ("conflict", false, Some(CONFLICT_SUGGESTION.to_string())); } - if normalized.contains("does not support") || normalized.contains("unsupported") { + if normalized.contains("does not support") + || normalized.contains("unsupported") + || normalized.contains("not yet supported") + { return ( "unsupported_feature", false, @@ -541,6 +544,30 @@ mod tests { } } + #[test] + fn test_error_descriptor_from_message_infers_conflict() { + let descriptor = ErrorDescriptor::from_message("Destination exists: report.json"); + let json = descriptor.to_json_output(); + + let details = json.details.expect("details should be present"); + assert_eq!(details.error_type, "conflict"); + assert!(!details.retryable); + assert_eq!(details.suggestion.as_deref(), Some(CONFLICT_SUGGESTION)); + } + + #[test] + fn test_error_descriptor_from_message_infers_unsupported_feature() { + let descriptor = ErrorDescriptor::from_message( + "Cross-alias S3-to-S3 copy not yet supported. Use download + upload.", + ); + let json = descriptor.to_json_output(); + + let details = json.details.expect("details should be present"); + assert_eq!(details.error_type, "unsupported_feature"); + assert!(!details.retryable); + assert_eq!(details.suggestion.as_deref(), Some(UNSUPPORTED_SUGGESTION)); + } + #[test] fn test_error_with_suggestion_overrides_default_hint() { let descriptor = ErrorDescriptor::from_code(