fuzzy-from-json-value 0.1.0

todo: write a description here
Documentation
// ---------------- [ File: fuzzy-from-json-value/src/fuzzy_u8.rs ]
crate::ix!();

pub fn fuzzy_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
where
    D: Deserializer<'de>,
{
    info!("fuzzy_u8 called");

    // First parse the raw JSON into a `JsonValue`.
    let val: JsonValue = Deserialize::deserialize(deserializer)?;

    match val {
        JsonValue::Number(num) => {

            trace!("fuzzy_u8 called with num: {num}");

            // (1) If it’s an integer that fits in u64
            if let Some(u) = num.as_u64() {
                if u <= 255 {
                    Ok(u as u8)
                } else {
                    // out of range => clamp
                    warn!("JsonValue {} exceeds u8 max => clamping to 255", u);
                    Ok(255)
                }
            }
            // (2) If it’s a float (f64)
            else if let Some(f) = num.as_f64() {
                if f < 0.0 {
                    warn!("JsonValue {} < 0 => clamping to 0", f);
                    return Ok(0);
                } else if f > 255.0 {
                    warn!("JsonValue {} > 255 => clamping to 255", f);
                    return Ok(255);
                } else {
                    // clamp the fraction by truncation
                    let truncated = f.trunc();
                    if (f - truncated).abs() > f64::EPSILON {
                        warn!("Truncating float {} => {}", f, truncated);
                    }
                    Ok(truncated as u8)
                }
            }
            // (3) Not an integer or float => error
            else {
                let msg = format!("Number is not a valid int or float => {:?}", num);
                Err(de::Error::custom(msg))
            }
        }
        other => {
            let msg = format!("Expected numeric, got {:?}", other);
            Err(de::Error::custom(msg))
        }
    }
}

/// Convert a single `&serde_json::Value` into a `u8` by applying the same “fuzzy”
/// clamp/truncation logic as your original `fuzzy_u8` function.
pub fn fuzzy_u8_from_value(val: &JsonValue) -> Result<u8, String> {
    match val {
        JsonValue::Number(num) => {
            // 1) integer?
            if let Some(u) = num.as_u64() {
                if u > 255 {
                    warn!("JsonValue {} > 255 => clamping to 255", u);
                    Ok(255)
                } else {
                    Ok(u as u8)
                }
            }
            // 2) float?
            else if let Some(f) = num.as_f64() {
                if f < 0.0 {
                    warn!("Float {} < 0 => clamping to 0", f);
                    Ok(0)
                } else if f > 255.0 {
                    warn!("Float {} > 255 => clamping to 255", f);
                    Ok(255)
                } else {
                    let truncated = f.trunc();
                    if (f - truncated).abs() > f64::EPSILON {
                        warn!("Truncating float {} => {}", f, truncated);
                    }
                    Ok(truncated as u8)
                }
            } else {
                Err(format!("Number is not convertible to u64/f64 => {:?}", num))
            }
        }
        other => Err(format!("Expected numeric for fuzzy_u8, got {:?}", other)),
    }
}

/// Convert a string key into a `u8` by fuzzily parsing it as a float or int,
/// then clamping to [0..255], logging if we do so.
pub fn fuzzy_key_to_u8(key: &str) -> Result<u8, String> {
    match key.parse::<f64>() {
        Ok(f) => {
            if f < 0.0 {
                warn!("fuzzy_key_to_u8: negative => clamping to 0 for key='{}'", key);
                Ok(0)
            } else if f > 255.0 {
                warn!("fuzzy_key_to_u8: above 255 => clamping to 255 for key='{}'", key);
                Ok(255)
            } else {
                let truncated = f.trunc();
                if (f - truncated).abs() > f64::EPSILON {
                    warn!("fuzzy_key_to_u8: truncating {} => {}", f, truncated);
                }
                Ok(truncated as u8)
            }
        }
        Err(e) => {
            let msg = format!("fuzzy_key_to_u8: cannot parse '{}' => {}", key, e);
            Err(msg)
        }
    }
}