use lastfm_edit::{ClientEvent, LastFmEditClientImpl};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let username =
env::var("LASTFM_EDIT_USERNAME").expect("Set LASTFM_EDIT_USERNAME environment variable");
let password =
env::var("LASTFM_EDIT_PASSWORD").expect("Set LASTFM_EDIT_PASSWORD environment variable");
println!("🔧 Demonstrating shared event broadcasting between clients...");
let http_client = http_client::native::NativeClient::new();
println!("🔐 Logging in with client1...");
let client1 =
LastFmEditClientImpl::login_with_credentials(Box::new(http_client), &username, &password)
.await?;
println!("✅ Successfully logged in as: {}", client1.username());
let http_client2 = http_client::native::NativeClient::new();
let client2 = client1.with_shared_broadcaster(Box::new(http_client2));
println!("🔄 Created client2 with shared broadcaster from client1");
let mut events1 = client1.subscribe();
let mut events2 = client2.subscribe();
println!("📡 Subscribed to events from both clients");
let monitor1 = tokio::spawn(async move {
println!("🔍 Client1 monitor started");
while let Ok(event) = events1.recv().await {
match event {
ClientEvent::RequestStarted { request } => {
println!(
"🚀 Client1 monitor: Started request {}",
request.short_description()
);
}
ClientEvent::RequestCompleted {
request,
status_code,
duration_ms,
} => {
println!(
"✅ Client1 monitor: Completed {} - {} ({} ms)",
request.short_description(),
status_code,
duration_ms
);
}
ClientEvent::RateLimited {
delay_seconds,
request,
rate_limit_type,
rate_limit_timestamp,
} => {
let req_desc = request
.as_ref()
.map(|r| r.short_description())
.unwrap_or_else(|| "unknown request".to_string());
println!(
"⏳ Client1 monitor: Rate limited ({rate_limit_type:?}) for {delay_seconds} seconds - {req_desc} (at timestamp {rate_limit_timestamp})"
);
}
ClientEvent::RateLimitEnded {
request,
rate_limit_type,
total_rate_limit_duration_seconds,
} => {
println!(
"🎉 Client1 monitor: Rate limiting ended ({rate_limit_type:?}) after {total_rate_limit_duration_seconds} seconds - {}",
request.short_description()
);
}
ClientEvent::Delaying {
delay_ms,
reason,
request,
delay_timestamp,
} => {
let req_desc = request
.as_ref()
.map(|r| r.short_description())
.unwrap_or_else(|| "unknown request".to_string());
println!(
"⏳ Client1 monitor: Delaying ({reason:?}) for {delay_ms}ms - {req_desc} (at timestamp {delay_timestamp})"
);
}
ClientEvent::EditAttempted {
edit,
success,
error_message,
duration_ms,
} => {
if success {
println!(
"✅ Client1 monitor: Edit succeeded '{}' -> '{}' ({duration_ms} ms)",
edit.track_name_original, edit.track_name
);
} else {
let error_msg = error_message
.as_ref()
.map(|s| format!(" - {s}"))
.unwrap_or_default();
println!(
"❌ Client1 monitor: Edit failed '{}' -> '{}' ({duration_ms} ms){error_msg}",
edit.track_name_original, edit.track_name
);
}
}
}
}
});
let monitor2 = tokio::spawn(async move {
println!("🔍 Client2 monitor started");
while let Ok(event) = events2.recv().await {
match event {
ClientEvent::RequestStarted { request } => {
println!(
"🚀 Client2 monitor: Started request {}",
request.short_description()
);
}
ClientEvent::RequestCompleted {
request,
status_code,
duration_ms,
} => {
println!(
"✅ Client2 monitor: Completed {} - {} ({} ms)",
request.short_description(),
status_code,
duration_ms
);
}
ClientEvent::RateLimited {
delay_seconds,
request,
rate_limit_type,
rate_limit_timestamp,
} => {
let req_desc = request
.as_ref()
.map(|r| r.short_description())
.unwrap_or_else(|| "unknown request".to_string());
println!(
"⏳ Client2 monitor: Rate limited ({rate_limit_type:?}) for {delay_seconds} seconds - {req_desc} (at timestamp {rate_limit_timestamp})"
);
}
ClientEvent::RateLimitEnded {
request,
rate_limit_type,
total_rate_limit_duration_seconds,
} => {
println!(
"🎉 Client2 monitor: Rate limiting ended ({rate_limit_type:?}) after {total_rate_limit_duration_seconds} seconds - {}",
request.short_description()
);
}
ClientEvent::Delaying {
delay_ms,
reason,
request,
delay_timestamp,
} => {
let req_desc = request
.as_ref()
.map(|r| r.short_description())
.unwrap_or_else(|| "unknown request".to_string());
println!(
"⏳ Client2 monitor: Delaying ({reason:?}) for {delay_ms}ms - {req_desc} (at timestamp {delay_timestamp})"
);
}
ClientEvent::EditAttempted {
edit,
success,
error_message,
duration_ms,
} => {
if success {
println!(
"✅ Client2 monitor: Edit succeeded '{}' -> '{}' ({duration_ms} ms)",
edit.track_name_original, edit.track_name
);
} else {
let error_msg = error_message
.as_ref()
.map(|s| format!(" - {s}"))
.unwrap_or_default();
println!(
"❌ Client2 monitor: Edit failed '{}' -> '{}' ({duration_ms} ms){error_msg}",
edit.track_name_original, edit.track_name
);
}
}
}
}
});
println!("📡 Making request with client1...");
match client1.get_recent_scrobbles(1).await {
Ok(tracks) => {
println!("✅ Client1 got {} tracks", tracks.len());
}
Err(e) => {
println!("⚠️ Client1 error: {e}");
}
}
let event1 = client1.latest_event();
let event2 = client2.latest_event();
match (event1, event2) {
(
Some(ClientEvent::RateLimited {
delay_seconds: delay1,
..
}),
Some(ClientEvent::RateLimited {
delay_seconds: delay2,
..
}),
) => {
println!("🎯 Both clients show rate limiting: {delay1}s and {delay2}s");
if delay1 == delay2 {
println!(
"✅ SUCCESS: Both clients report the same delay (shared broadcaster working!)"
);
} else {
println!("❌ UNEXPECTED: Different delays reported");
}
}
(
Some(ClientEvent::RequestCompleted { .. }),
Some(ClientEvent::RequestCompleted { .. }),
) => {
println!("✅ Both clients show completed requests (shared broadcaster working!)");
}
(
Some(ClientEvent::EditAttempted {
success: success1, ..
}),
Some(ClientEvent::EditAttempted {
success: success2, ..
}),
) => {
if success1 == success2 {
println!("✅ Both clients show same edit result (shared broadcaster working!)");
} else {
println!("❌ UNEXPECTED: Different edit results reported");
}
}
(None, None) => {
println!("📊 No events occurred yet - this is normal");
println!(" In real usage, both clients would see the same events when they occur");
}
_ => {
println!("📊 Different event states between clients (could be due to timing)");
}
}
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
monitor1.abort();
monitor2.abort();
println!("🏁 Demo completed!");
println!("\n📄 Key Points:");
println!(" • client1.with_shared_broadcaster() creates clients that share event broadcasting");
println!(" • When any shared client encounters rate limiting, all see the same events");
println!(" • Use this pattern when you need multiple HTTP clients but want unified rate limit handling");
Ok(())
}