How to fetch json with reqwest and handle with thiserror

💡 full source code is on github

foo.json

[
  {
    "id": "foo",
    "type": "Cat",
    "weight": 123.45,
    "createdAt": "2022-09-01"
  },
  {
    "id": "bar",
    "type": "Duck",
    "weight": 42.2424,
    "createdAt": "2022-08-01"
  }
]

Cargo.toml

[package]
name = "fetch-json-reqwest-thiserror"
version = "0.1.0"
edition = "2021"

[dependencies]
# Use No-std support https://serde.rs/no-std.html
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = "1.0"

# Use foe fetch, feature `json` for load json, `rustls-tls` for load via `TLS`.
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] }

# Use for handy return Result.
anyhow = "1.0.65"

# Use for async.
tokio = { version ="1.22", features = ["full"] }
thiserror = "1.0.50"

main.rs

use serde::{Deserialize, Serialize};
use thiserror::Error;

#[derive(Serialize, Deserialize, Debug)]
// This will auto convert form `camelCase` to `snake_case`
#[serde(rename_all = "camelCase")]
struct AnimalData {
    id: String,
    weight: f32,
    // This will auto convert form "createdAt" to `created_at`
    created_at: String,
}

// 👇 Derive from thiserror::Error
#[derive(Error, Debug)]
enum CustomError {
    #[error("Request failed: {0}")]
    RequestError(reqwest::Error),

    #[error("JSON parsing error: {0}")]
    JsonParseError(reqwest::Error),
}

async fn fetch(url: &str) -> Result<Vec<AnimalData>, CustomError> {
    let response = reqwest::get(url).await.map_err(CustomError::RequestError)?;
    let animals = response
        .json::<Vec<AnimalData>>()
        .await
        .map_err(CustomError::JsonParseError)?;

    Ok(animals)
}

#[tokio::main]
async fn main() {
    let animals = match fetch("https://raw.githubusercontent.com/gist-rs/book/main/examples/r4/20-fetch-json-reqwest/src/foo.json").await {
        Ok(animals) => {
            println!("{animals:#?}");
            // Will return parsed JSON as Vec<AnimalData> type.
            animals
        }
        Err(err) => {
            // Will yelling.
            println!("No animals!: {:?}", err);
            
            // Will return empty vector animals.
            Vec::from([])
        }
    };

    println!("{animals:#?}");
}

🤷 reqwest+TLS is not runnable via Rust Playground so output is shown below.

Run
Ok(
    [
        AnimalData {
            id: "foo",
            weight: 123.45,
            created_at: "2022-09-01",
        },
        AnimalData {
            id: "bar",
            weight: 42.2424,
            created_at: "2022-08-01",
        },
    ],
)