alpaca-core 0.27.1

Shared primitives for the alpaca-rust workspace
Documentation
use serde::{Deserialize, Deserializer, de};

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum StringOrNumber {
    String(String),
    Number(serde_json::Number),
}

fn parse_f64<E>(value: StringOrNumber) -> Result<f64, E>
where
    E: de::Error,
{
    let raw = match value {
        StringOrNumber::String(value) => value,
        StringOrNumber::Number(value) => value.to_string(),
    };
    let number = raw
        .parse::<f64>()
        .map_err(|error| E::custom(format!("invalid float value `{raw}`: {error}")))?;

    if number.is_finite() {
        Ok(number)
    } else {
        Err(E::custom(format!("float value must be finite: `{raw}`")))
    }
}

pub fn round(value: f64, scale: u32) -> f64 {
    if !value.is_finite() {
        return value;
    }

    let multiplier = 10_f64.powi(scale as i32);
    (value * multiplier).round() / multiplier
}

pub fn deserialize_option_f64_from_string_or_number<'de, D>(
    deserializer: D,
) -> Result<Option<f64>, D::Error>
where
    D: Deserializer<'de>,
{
    Option::<StringOrNumber>::deserialize(deserializer)?
        .map(parse_f64)
        .transpose()
}