use busbar_sf_auth::{Credentials, SalesforceCredentials};
use busbar_sf_client::ErrorKind;
use busbar_sf_rest::SalesforceRestClient;
use std::time::Duration;
use tokio::time::sleep;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Salesforce Error Handling Examples ===\n");
let creds = get_credentials().await?;
let client = SalesforceRestClient::new(creds.instance_url(), creds.access_token())?;
example_basic_error_handling(&client).await;
example_retry_logic(&client).await;
example_rate_limit_handling(&client).await;
example_auth_error_detection(&client).await;
example_error_categorization().await;
println!("\n✓ All error handling examples completed!");
Ok(())
}
async fn example_basic_error_handling(client: &SalesforceRestClient) {
println!("Example 1: Basic Error Handling");
println!("--------------------------------");
let result: Result<serde_json::Value, _> = client
.get("Account", "001000000000000", None) .await;
match result {
Ok(account) => {
println!("✓ Found account: {:?}", account);
}
Err(e) => {
println!("✗ Error occurred: {}", e);
println!(" Error type: {:?}", e.kind);
if matches!(e.kind, busbar_sf_rest::ErrorKind::Salesforce { .. }) {
println!(" This is a Salesforce API error");
}
}
}
println!();
}
async fn example_retry_logic(client: &SalesforceRestClient) {
println!("Example 2: Retry Logic");
println!("----------------------");
let max_retries = 3;
let mut attempt = 0;
loop {
attempt += 1;
println!("Attempt {}/{}", attempt, max_retries);
let result: Result<Vec<serde_json::Value>, _> = client
.query_all("SELECT Id, Name FROM Account LIMIT 10")
.await;
match result {
Ok(accounts) => {
println!("✓ Successfully retrieved {} accounts", accounts.len());
break;
}
Err(e) => {
println!("✗ Error: {}", e);
if attempt < max_retries {
let backoff = Duration::from_secs(2u64.pow(attempt - 1));
println!(" Retrying after {:?}...", backoff);
sleep(backoff).await;
} else {
println!(" Max retries reached");
break;
}
}
}
}
println!();
}
async fn example_rate_limit_handling(client: &SalesforceRestClient) {
println!("Example 3: Rate Limit Handling");
println!("-------------------------------");
match client.limits().await {
Ok(limits) => {
println!("✓ Retrieved org limits");
if let Some(daily_api) = limits.get("DailyApiRequests") {
if let (Some(max), Some(remaining)) = (
daily_api.get("Max").and_then(|v| v.as_i64()),
daily_api.get("Remaining").and_then(|v| v.as_i64()),
) {
let usage_percent = ((max - remaining) as f64 / max as f64) * 100.0;
println!(
" Daily API Usage: {:.1}% ({}/{})",
usage_percent,
max - remaining,
max
);
if usage_percent > 80.0 {
println!(" ⚠ Warning: API usage is above 80%!");
}
}
}
}
Err(e) => {
println!("✗ Error retrieving limits: {}", e);
println!(" Note: Check if rate limited or other error occurred");
}
}
println!();
}
async fn example_auth_error_detection(client: &SalesforceRestClient) {
println!("Example 4: Authentication Error Detection");
println!("------------------------------------------");
let result: Result<Vec<serde_json::Value>, _> =
client.query_all("SELECT Id FROM Account LIMIT 1").await;
match result {
Ok(_) => {
println!("✓ Authentication is valid");
}
Err(e) => {
println!("✗ Error: {}", e);
if matches!(e.kind, busbar_sf_rest::ErrorKind::Auth(_)) {
println!(" This is an authentication error!");
println!(" Action: Refresh access token or re-authenticate");
}
}
}
println!();
}
async fn example_error_categorization() {
println!("Example 5: Error Categorization");
println!("--------------------------------");
let errors = vec![
(
"Rate Limited",
ErrorKind::RateLimited {
retry_after: Some(Duration::from_secs(30)),
},
),
("Timeout", ErrorKind::Timeout),
(
"Authentication",
ErrorKind::Authentication("Invalid token".to_string()),
),
("Not Found", ErrorKind::NotFound("Account".to_string())),
(
"Connection",
ErrorKind::Connection("Network error".to_string()),
),
];
for (name, error_kind) in errors {
println!("\n{} Error:", name);
println!(" Retryable: {}", error_kind.is_retryable());
if let ErrorKind::RateLimited {
retry_after: Some(duration),
} = error_kind
{
println!(" Retry after: {:?}", duration);
}
}
println!();
}
#[allow(dead_code)]
async fn create_account_with_context(
client: &SalesforceRestClient,
name: &str,
) -> Result<String, String> {
let account = serde_json::json!({
"Name": name
});
client.create("Account", &account).await.map_err(|e| {
format!("Failed to create account '{}': {}", name, e)
})
}
#[allow(dead_code)]
async fn query_with_fallback(
client: &SalesforceRestClient,
) -> Result<Vec<serde_json::Value>, Box<dyn std::error::Error>> {
let result = client
.query_all("SELECT Id, Name, CustomField__c FROM Account")
.await;
match result {
Ok(accounts) => Ok(accounts),
Err(e) => {
if let busbar_sf_rest::ErrorKind::Salesforce { error_code, .. } = &e.kind {
if error_code == "INVALID_FIELD" {
println!(" CustomField__c doesn't exist, trying without it...");
return client
.query_all("SELECT Id, Name FROM Account")
.await
.map_err(Into::into);
}
}
Err(e.into())
}
}
}
#[allow(dead_code)]
async fn process_with_partial_failures(
client: &SalesforceRestClient,
account_ids: Vec<String>,
) -> (Vec<serde_json::Value>, Vec<String>) {
let mut successful = Vec::new();
let mut failed = Vec::new();
for id in account_ids {
match client.get::<serde_json::Value>("Account", &id, None).await {
Ok(account) => successful.push(account),
Err(e) => {
eprintln!("Failed to get account {}: {}", id, e);
failed.push(id);
}
}
}
(successful, failed)
}
#[allow(dead_code)]
async fn with_exponential_backoff<F, Fut, T>(
mut operation: F,
max_retries: u32,
) -> Result<T, Box<dyn std::error::Error>>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = Result<T, Box<dyn std::error::Error>>>,
{
let mut attempt = 0;
loop {
match operation().await {
Ok(result) => return Ok(result),
Err(e) => {
attempt += 1;
if attempt >= max_retries {
return Err(e);
}
let backoff = Duration::from_millis(100 * 2u64.pow(attempt - 1));
println!(
" Attempt {} failed, retrying after {:?}...",
attempt, backoff
);
sleep(backoff).await;
}
}
}
}
async fn get_credentials() -> Result<SalesforceCredentials, Box<dyn std::error::Error>> {
if let Ok(creds) = SalesforceCredentials::from_sfdx_alias("default").await {
println!("✓ Using credentials from Salesforce CLI\n");
return Ok(creds);
}
match SalesforceCredentials::from_env() {
Ok(creds) => {
println!("✓ Using credentials from environment variables\n");
Ok(creds)
}
Err(e) => {
eprintln!("✗ Failed to load credentials: {}", e);
eprintln!("\nPlease either:");
eprintln!(" 1. Authenticate with Salesforce CLI: sf org login web");
eprintln!(" 2. Set environment variables: SF_INSTANCE_URL, SF_ACCESS_TOKEN");
Err(e.into())
}
}
}