137 lines
6.1 KiB
Text
137 lines
6.1 KiB
Text
#!/usr/bin/env nu
|
|
|
|
# Provider Error Handling - Standardized error handling across providers
|
|
#
|
|
# This module consolidates error handling patterns:
|
|
# - Command execution with error handling
|
|
# - Error classification
|
|
# - Debug vs production output
|
|
# - Retry logic
|
|
|
|
# Execute command with structured error handling
|
|
export def provider_execute_safe [cmd: string, context: string = "command", debug_on_error: bool = true, suppress_throw: bool = false] {
|
|
let result = (^$cmd | complete)
|
|
{exit_code: $result.exit_code, stdout: $result.stdout, stderr: $result.stderr, success: ($result.exit_code == 0), command: $cmd, context: $context}
|
|
}
|
|
|
|
# Check command result and handle error
|
|
export def provider_check_result [result: record, error_exit: bool = false, throw_error: bool = false] {
|
|
if not $result.success {
|
|
if (is-debug-enabled) {
|
|
print $"Error in ($result.context):"
|
|
print $"Exit code: ($result.exit_code)"
|
|
if $result.stderr != "" { print $"Stderr: ($result.stderr)" }
|
|
if $result.stdout != "" { print $"Stdout: ($result.stdout)" }
|
|
} else {
|
|
print $"Error ($result.context): ($result.exit_code)"
|
|
if ($result.stdout | str contains "error") {
|
|
print $result.stdout | ^grep "error"
|
|
}
|
|
}
|
|
if $throw_error {
|
|
(throw-error $"Error: ($result.context)" $result.stderr --span (metadata $result).span)
|
|
}
|
|
if $error_exit { exit 1 }
|
|
return false
|
|
}
|
|
true
|
|
}
|
|
|
|
# Categorize error by type
|
|
export def provider_classify_error [exit_code: int, stderr: string = ""] {
|
|
if $exit_code == 0 { "success" }
|
|
else if ($stderr | str contains "Not found") { "not_found" }
|
|
else if ($stderr | str contains "Permission denied") { "permission" }
|
|
else if ($stderr | str contains "Timeout") { "timeout" }
|
|
else if ($stderr | str contains "Connection") { "network" }
|
|
else if ($stderr | str contains "401") { "auth" }
|
|
else if ($stderr | str contains "403") { "forbidden" }
|
|
else if ($stderr | str contains "409") { "conflict" }
|
|
else if $exit_code == 127 { "not_found" }
|
|
else if $exit_code > 0 { "general_error" }
|
|
else { "unknown" }
|
|
}
|
|
|
|
# Handle specific error types
|
|
export def provider_handle_error_type [error_type: string, context: string = "", recoverable: bool = false] {
|
|
match $error_type {
|
|
"not_found" => { print $"Error: Resource not found ($context)"; false },
|
|
"permission" => { print $"Error: Permission denied ($context)"; false },
|
|
"timeout" => { print $"Error: Timeout ($context)"; $recoverable },
|
|
"network" => { print $"Error: Network error ($context)"; $recoverable },
|
|
"auth" => { print $"Error: Authentication failed ($context)"; false },
|
|
"forbidden" => { print $"Error: Access forbidden ($context)"; false },
|
|
"conflict" => { print $"Error: Resource conflict ($context)"; $recoverable },
|
|
_ => { print $"Error: ($error_type) in ($context)"; false }
|
|
}
|
|
}
|
|
|
|
# Execute with retry logic
|
|
export def provider_execute_with_retry [cmd: string, max_retries: int = 3, retry_delay_secs: int = 5, context: string = "command"] {
|
|
mut result = (provider_execute_safe $cmd $context)
|
|
mut retry_count = 0
|
|
while (not $result.success) and $retry_count < $max_retries {
|
|
let error_type = (provider_classify_error $result.exit_code $result.stderr)
|
|
let should_retry = (provider_handle_error_type $error_type $context true)
|
|
if $should_retry {
|
|
$retry_count = $retry_count + 1
|
|
let ratio = $"($retry_count)/($max_retries)"
|
|
print $"Retrying... ($ratio)"
|
|
sleep ($"($retry_delay_secs)sec" | into duration)
|
|
$result = (provider_execute_safe $cmd $context)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
$result | insert retry_count $retry_count
|
|
}
|
|
|
|
# Validate response structure
|
|
export def provider_validate_response [response: record, required_fields: list<string> = []] {
|
|
if ($response | is-empty) { print "Error: Empty response"; return false }
|
|
if ($required_fields | is-empty) { return true }
|
|
let missing = $required_fields | where {|field| not ($response | has $field)}
|
|
if not ($missing | is-empty) { print $"Error: Missing required fields: ($missing | str join ', ')"; return false }
|
|
true
|
|
}
|
|
|
|
# Extract error message from response
|
|
export def provider_extract_error_message [response: record | string, error_path: string = "error"] {
|
|
if ($response | describe) == "string" {
|
|
$response
|
|
} else if ($response | has $error_path) {
|
|
$response | get $error_path | into string
|
|
} else if ($response | has "message") {
|
|
$response | get "message" | into string
|
|
} else if ($response | has "error_message") {
|
|
$response | get "error_message" | into string
|
|
} else if ($response | has "error") {
|
|
let err = ($response | get "error")
|
|
if ($err | describe) == "string" { $err } else { ($err | into string) }
|
|
} else {
|
|
($response | into string)
|
|
}
|
|
}
|
|
|
|
# Check for common API errors in response
|
|
export def provider_check_api_error [response: record | string] {
|
|
let error_indicators = ["error", "Error", "ERROR", "failed", "Failed", "invalid", "not found"]
|
|
let has_error = if ($response | describe) == "string" {
|
|
$error_indicators | any {|ind| $response | str contains $ind}
|
|
} else if ($response | is-empty) { true } else { false }
|
|
let error_message = if $has_error { (provider_extract_error_message $response "error") } else { "" }
|
|
{has_error: $has_error, message: $error_message}
|
|
}
|
|
|
|
# Suppress output on success
|
|
export def provider_output_device [suppress: bool = false] {
|
|
if $suppress { if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" } } else { "" }
|
|
}
|
|
|
|
# Format error for user display
|
|
export def provider_format_error [error_type: string, context: string, message: string = "", suggest_retry: bool = false] {
|
|
let header = $"Error: ($error_type) in ($context)"
|
|
let body = if $message != "" { $"($message)" } else { "" }
|
|
let suggestion = if $suggest_retry { "Retry may help." } else { "" }
|
|
[$header, $body, $suggestion] | where {|s| $s != "" } | str join "\n"
|
|
}
|