Repository: astral-sh/uv
File: crates/uv-client/src/cached_client.rs (Rust)

Code before:
use rkyv::util::AlignedVec;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use tracing::{Instrument, debug, info_span, instrument, trace, warn};

use uv_cache::{CacheEntry, Freshness};
use uv_fs::write_atomic;
use uv_redacted::DisplaySafeUrl;

use crate::BaseClient;
use crate::base_client::is_transient_network_error;
use crate::error::ProblemDetails;
use crate::{
    Error, ErrorKind,
    httpcache::{AfterResponse, BeforeRequest, CachePolicy, CachePolicyBuilder},
    rkyvutil::OwnedArchive,
};

/// Extract problem details from an HTTP response if it has the correct content type
///
/// Note: This consumes the response body, so it should only be called when there's an error status.
async fn extract_problem_details(response: Response) -> Option<ProblemDetails> {
    if let Ok(bytes) = response.bytes().await {
        serde_json::from_slice(&bytes).ok()
    } else {
        warn!("Failed to read response body for problem details");
        None
    }
}

/// A trait the generalizes (de)serialization at a high level.
///
/// The main purpose of this trait is to make the `CachedClient` work for
/// either serde or other mechanisms of serialization such as `rkyv`.
///
/// If you're using Serde, then unless you want to control the format, callers
/// should just use `CachedClient::get_serde`. This will use a default
/// implementation of `Cacheable` internally.
///
/// Alternatively, callers using `rkyv` should use
/// `CachedClient::get_cacheable`. If your types fit into the
/// `rkyvutil::OwnedArchive` mold, then an implementation of `Cacheable` is
/// already provided for that type.
pub trait Cacheable: Sized {
    /// This associated type permits customizing what the "output" type of
    /// deserialization is. It can be identical to `Self`.
    ///
    /// Typical use of this is for wrapper types used to provide blanket trait
    /// impls without hitting overlapping impl problems.
    type Target: Send + 'static;


Review comment (suggestion):
We should show the inner error in this warning (at least for serde), so it's clear why e.g. the data didn't match the schema.

Code after:
use rkyv::util::AlignedVec;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use tracing::{Instrument, debug, info_span, instrument, trace, warn};

use uv_cache::{CacheEntry, Freshness};
use uv_fs::write_atomic;
use uv_redacted::DisplaySafeUrl;

use crate::BaseClient;
use crate::base_client::is_transient_network_error;
use crate::error::ProblemDetails;
use crate::{
    Error, ErrorKind,
    httpcache::{AfterResponse, BeforeRequest, CachePolicy, CachePolicyBuilder},
    rkyvutil::OwnedArchive,
};

/// Extract problem details from an HTTP response if it has the correct content type
///
/// Note: This consumes the response body, so it should only be called when there's an error status.
async fn extract_problem_details(response: Response) -> Option<ProblemDetails> {
    match response.bytes().await {
        Ok(bytes) => match serde_json::from_slice(&bytes) {
            Ok(details) => Some(details),
            Err(err) => {
                warn!("Failed to parse problem details: {err}");
                None
            }
        },
        Err(err) => {
            warn!("Failed to read response body for problem details: {err}");
            None
        }
    }
}

/// A trait the generalizes (de)serialization at a high level.
///
/// The main purpose of this trait is to make the `CachedClient` work for
/// either serde or other mechanisms of serialization such as `rkyv`.
///
/// If you're using Serde, then unless you want to control the format, callers
/// should just use `CachedClient::get_serde`. This will use a default
/// implementation of `Cacheable` internally.
///
/// Alternatively, callers using `rkyv` should use
/// `CachedClient::get_cacheable`. If your types fit into the
/// `rkyvutil::OwnedArchive` mold, then an implementation of `Cacheable` is
/// already provided for that type.
pub trait Cacheable: Sized {