async-stripe-types 1.0.0-rc.5

API bindings for the Stripe HTTP API
Documentation
#![allow(missing_debug_implementations)]
use std::collections::HashMap;
use std::mem;
use std::str::FromStr;

use miniserde::Deserialize;
use miniserde::de::Visitor;
use miniserde::json::{Number, Object, Value};

use crate::Currency;

#[derive(Default)]
pub struct ObjectBuilderInner {
    object: Object,
    key: Option<String>,
    value: Option<Value>,
}

impl ObjectBuilderInner {
    pub fn shift(&mut self) {
        if let (Some(k), Some(v)) = (self.key.take(), self.value.take()) {
            self.object.insert(k, v);
        }
    }

    pub fn key_inner(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor> {
        self.shift();
        self.key = Some(k.to_owned());
        Ok(Deserialize::begin(&mut self.value))
    }

    pub fn finish_inner(&mut self) -> Option<(String, Object)> {
        self.shift();
        let object_key = self.object.get("object").and_then(|o| match o {
            Value::String(object_key) => Some(object_key.clone()),
            _ => None,
        })?;
        let final_object = mem::replace(&mut self.object, Object::new());
        Some((object_key, final_object))
    }
}

#[derive(Default)]
pub struct MaybeDeletedBuilderInner {
    object: Object,
    key: Option<String>,
    value: Option<Value>,
}

impl MaybeDeletedBuilderInner {
    pub fn shift(&mut self) {
        if let (Some(k), Some(v)) = (self.key.take(), self.value.take()) {
            self.object.insert(k, v);
        }
    }

    pub fn key_inner(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor> {
        self.shift();
        self.key = Some(k.to_owned());
        Ok(Deserialize::begin(&mut self.value))
    }

    pub fn finish_inner(&mut self) -> Option<(bool, Object)> {
        self.shift();
        let deleted = self
            .object
            .get("deleted")
            .map(|o| match o {
                Value::Bool(bool) => *bool,
                _ => false,
            })
            .unwrap_or(false);
        let final_object = mem::replace(&mut self.object, Object::new());
        Some((deleted, final_object))
    }
}

pub trait FromValueOpt {
    fn from_value(v: Value) -> Option<Self>
    where
        Self: Sized;
}

impl FromValueOpt for bool {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Bool(b) => Some(b),
            _ => None,
        }
    }
}

impl FromValueOpt for i64 {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Number(num) => match num {
                Number::U64(u) => Some(u as i64),
                Number::I64(i) => Some(i),
                Number::F64(_) => None,
            },
            _ => None,
        }
    }
}

impl FromValueOpt for u64 {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Number(num) => match num {
                Number::U64(u) => Some(u),
                Number::I64(i) => Some(i.try_into().ok()?),
                Number::F64(_) => None,
            },
            _ => None,
        }
    }
}

impl FromValueOpt for f64 {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Number(num) => match num {
                Number::U64(u) => Some(u as f64),
                Number::I64(i) => Some(i as f64),
                Number::F64(f) => Some(f),
            },
            _ => None,
        }
    }
}
impl FromValueOpt for u32 {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Number(num) => match num {
                Number::U64(u) => Some(u.try_into().ok()?),
                Number::I64(i) => Some(i.try_into().ok()?),
                Number::F64(_) => None,
            },
            _ => None,
        }
    }
}
impl FromValueOpt for u8 {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Number(num) => match num {
                Number::U64(u) => Some(u.try_into().ok()?),
                Number::I64(i) => Some(i.try_into().ok()?),
                Number::F64(_) => None,
            },
            _ => None,
        }
    }
}

impl FromValueOpt for String {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::String(str) => Some(str),
            _ => None,
        }
    }
}

impl<T: FromValueOpt> FromValueOpt for Option<T> {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Null => Some(None),
            val => Some(T::from_value(val)),
        }
    }
}

impl<T: FromValueOpt> FromValueOpt for Vec<T> {
    fn from_value(v: Value) -> Option<Self> {
        match v {
            Value::Array(items) => items.into_iter().map(|i| T::from_value(i)).collect(),
            _ => None,
        }
    }
}

impl<V: FromValueOpt> FromValueOpt for HashMap<String, V> {
    fn from_value(v: Value) -> Option<Self> {
        let Value::Object(obj) = v else {
            return None;
        };
        obj.into_iter()
            .map(|(k, v)| {
                let v = V::from_value(v)?;
                Some((k, v))
            })
            .collect::<Option<HashMap<_, _>>>()
    }
}

impl<V: FromValueOpt> FromValueOpt for HashMap<Currency, V> {
    fn from_value(v: Value) -> Option<Self> {
        let Value::Object(obj) = v else {
            return None;
        };
        obj.into_iter()
            .map(|(k, v)| {
                let k = Currency::from_str(&k).ok()?;
                let v = V::from_value(v)?;
                Some((k, v))
            })
            .collect::<Option<HashMap<_, _>>>()
    }
}

pub fn extract_object_discr(value: Value) -> Option<(String, Object)> {
    let Value::Object(obj) = value else { return None };
    let obj_name = obj.get("object")?;
    let obj_str = match obj_name {
        Value::String(str) => str.to_string(),
        _ => return None,
    };
    Some((obj_str, obj))
}

impl FromValueOpt for Value {
    fn from_value(v: Value) -> Option<Self> {
        Some(v)
    }
}

impl<T: FromValueOpt> FromValueOpt for Box<T> {
    fn from_value(v: Value) -> Option<Self> {
        Some(Box::new(T::from_value(v)?))
    }
}

#[macro_export]
macro_rules! impl_from_val_with_from_str {
    ($struct_name:ident) => {
        impl $crate::miniserde_helpers::FromValueOpt for $struct_name {
            fn from_value(v: miniserde::json::Value) -> Option<Self> {
                match v {
                    miniserde::json::Value::String(str) => std::str::FromStr::from_str(&str).ok(),
                    _ => None,
                }
            }
        }
    };
}

impl_from_val_with_from_str!(Currency);