use cameo::{
CameoClient, TmdbConfig, TmdbError,
unified::{CameoClientError, DetailProvider, SearchProvider},
};
#[tokio::main]
async fn main() {
let _ = dotenvy::dotenv();
demo_build_time_errors();
demo_api_errors().await;
demo_not_found().await;
demo_propagation_styles().await;
#[cfg(feature = "anilist")]
demo_anilist_errors().await;
}
fn demo_build_time_errors() {
println!("=== Build-time errors ===");
match CameoClient::builder().build() {
Ok(_) => println!(" [unexpected] client built with no providers"),
Err(CameoClientError::NoProviders) => {
println!(" [ok] NoProviders: must configure at least one provider");
}
Err(e) => println!(" [unexpected] {e}"),
}
match CameoClient::builder()
.with_tmdb(TmdbConfig::new(""))
.build()
{
Ok(_) => println!(" [unexpected] empty token accepted"),
Err(CameoClientError::Tmdb(TmdbError::InvalidConfig(msg))) => {
println!(" [ok] InvalidConfig: {msg}");
}
Err(e) => println!(" [unexpected] {e}"),
}
println!();
}
async fn demo_api_errors() {
println!("=== API errors (invalid token → 401) ===");
let client = match CameoClient::builder()
.with_tmdb(TmdbConfig::new("invalid-token"))
.build()
{
Ok(c) => c,
Err(e) => {
println!(" build failed unexpectedly: {e}");
println!();
return;
}
};
match client.search_movies("Inception", None).await {
Ok(results) => {
println!(" [unexpected] got {} results", results.total_results);
}
Err(CameoClientError::Tmdb(TmdbError::Api { status, message })) => match status {
401 => println!(" [ok] 401 Unauthorized — check your API token: {message}"),
403 => println!(" [ok] 403 Forbidden — token lacks required permissions"),
404 => println!(" [ok] 404 Not Found — endpoint may have moved"),
429 => println!(" [ok] 429 Rate Limited — back off and retry"),
500..=599 => println!(" [ok] {status} Server Error — TMDB is having issues"),
_ => println!(" [ok] HTTP {status}: {message}"),
},
Err(CameoClientError::Tmdb(TmdbError::Http(e))) => {
println!(" transport error (no network?): {e}");
}
Err(e) => println!(" unexpected error variant: {e}"),
}
println!();
}
async fn demo_not_found() {
println!("=== Not-found recovery ===");
let token = match std::env::var("TMDB_API_TOKEN") {
Ok(t) => t,
Err(_) => {
println!(" skipped — TMDB_API_TOKEN not set");
println!();
return;
}
};
let client = match CameoClient::builder()
.with_tmdb(TmdbConfig::new(token))
.build()
{
Ok(c) => c,
Err(e) => {
println!(" build error: {e}");
println!();
return;
}
};
match client.movie_details(999_999_999).await {
Ok(details) => println!(" [unexpected] found: {}", details.movie.title),
Err(CameoClientError::Tmdb(TmdbError::Api { status: 404, .. })) => {
println!(" [ok] movie not found — returning default placeholder");
}
Err(e) => println!(" unexpected error: {e}"),
}
println!();
}
async fn demo_propagation_styles() {
println!("=== Propagation styles ===");
match search_with_question_mark("Inception").await {
Ok(count) => println!(" [?-style] found {count} results for 'Inception'"),
Err(e) => println!(" [?-style] skipped or error: {e}"),
}
search_with_match("Inception").await;
println!();
}
async fn search_with_question_mark(query: &str) -> Result<u32, Box<dyn std::error::Error>> {
let token = std::env::var("TMDB_API_TOKEN")?;
let client = CameoClient::builder()
.with_tmdb(TmdbConfig::new(token))
.build()?;
let results = client.search_movies(query, None).await?;
Ok(results.total_results)
}
async fn search_with_match(query: &str) {
let token = match std::env::var("TMDB_API_TOKEN") {
Ok(t) => t,
Err(_) => {
println!(" [match-style] skipped — TMDB_API_TOKEN not set");
return;
}
};
let client = match CameoClient::builder()
.with_tmdb(TmdbConfig::new(token))
.build()
{
Ok(c) => c,
Err(CameoClientError::NoProviders) => {
println!(" [match-style] no providers configured");
return;
}
Err(e) => {
println!(" [match-style] build error: {e}");
return;
}
};
match client.search_movies(query, None).await {
Ok(results) => {
println!(
" [match-style] found {} results for '{query}'",
results.total_results
);
}
Err(CameoClientError::Tmdb(TmdbError::Api { status: 401, .. })) => {
println!(" [match-style] auth failed — check TMDB_API_TOKEN");
}
Err(e) => {
println!(" [match-style] search failed: {e}");
}
}
}
#[cfg(feature = "anilist")]
async fn demo_anilist_errors() {
use cameo::{AniListConfig, AniListError};
println!("=== AniList errors ===");
let client = match CameoClient::builder()
.with_anilist(AniListConfig::new())
.build()
{
Ok(c) => c,
Err(e) => {
println!(" build error: {e}");
println!();
return;
}
};
match client.person_details(999_999_999).await {
Ok(details) => println!(" [unexpected] found: {}", details.person.name),
Err(CameoClientError::AniList(AniListError::NotFound)) => {
println!(" [ok] AniListError::NotFound — staff ID does not exist");
}
Err(CameoClientError::AniList(AniListError::GraphQL(errors))) => {
for e in &errors {
println!(" GraphQL error: {}", e.message);
}
}
Err(CameoClientError::AniList(AniListError::Http(e))) => {
println!(" transport error: {e}");
}
Err(e) => println!(" unexpected error: {e}"),
}
println!();
}