use std::ops::{Deref, DerefMut};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Params(IndexMap<String, Value>);
impl Params {
#[must_use]
pub fn new() -> Self {
Self(IndexMap::new())
}
#[must_use]
pub fn from_index_map(map: IndexMap<String, Value>) -> Self {
Self(map)
}
#[must_use]
pub fn get_u64(&self, key: &str) -> Option<u64> {
self.get(key).and_then(Value::as_u64)
}
#[must_use]
pub fn get_i64(&self, key: &str) -> Option<i64> {
self.get(key).and_then(Value::as_i64)
}
#[must_use]
pub fn get_usize(&self, key: &str) -> Option<usize> {
self.get(key)
.and_then(Value::as_u64)
.and_then(|n| usize::try_from(n).ok())
}
#[must_use]
pub fn get_str(&self, key: &str) -> Option<&str> {
self.get(key).and_then(|v| v.as_str())
}
#[must_use]
pub fn get_bool(&self, key: &str) -> Option<bool> {
self.get(key).and_then(Value::as_bool)
}
#[must_use]
pub fn get_f64(&self, key: &str) -> Option<f64> {
self.get(key).and_then(Value::as_f64)
}
#[cfg(feature = "python")]
pub fn to_pydict(&self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::Py<pyo3::types::PyDict>> {
crate::python::params::params_to_pydict(py, self)
}
}
impl Deref for Params {
type Target = IndexMap<String, Value>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Params {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> IntoIterator for &'a Params {
type Item = (&'a String, &'a Value);
type IntoIter = indexmap::map::Iter<'a, String, Value>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[cfg(feature = "python")]
pub fn from_pydict(
py: pyo3::Python<'_>,
dict: &pyo3::Py<pyo3::types::PyDict>,
) -> pyo3::PyResult<Option<Params>> {
crate::python::params::pydict_to_params(py, dict)
}
#[cfg(test)]
mod tests {
use rstest::*;
use serde_json::json;
use super::Params;
fn create_test_params() -> Params {
let mut params = Params::new();
params.insert("u64_val".to_string(), json!(42u64));
params.insert("i64_val".to_string(), json!(-100i64));
params.insert("usize_val".to_string(), json!(5u64));
params.insert("str_val".to_string(), json!("hello"));
params.insert("bool_val".to_string(), json!(true));
params.insert("f64_val".to_string(), json!(2.5));
params
}
#[rstest]
fn test_params_getters() {
let params = create_test_params();
assert_eq!(params.get_u64("u64_val"), Some(42));
assert_eq!(params.get_i64("i64_val"), Some(-100));
assert_eq!(params.get_usize("usize_val"), Some(5));
assert_eq!(params.get_str("str_val"), Some("hello"));
assert_eq!(params.get_bool("bool_val"), Some(true));
assert_eq!(params.get_f64("f64_val"), Some(2.5));
assert_eq!(params.get_u64("missing"), None);
assert_eq!(params.get_i64("missing"), None);
assert_eq!(params.get_usize("missing"), None);
assert_eq!(params.get_str("missing"), None);
assert_eq!(params.get_bool("missing"), None);
assert_eq!(params.get_f64("missing"), None);
assert_eq!(params.get_u64("str_val"), None);
assert_eq!(params.get_str("u64_val"), None);
}
#[rstest]
fn test_params_ref_get_usize_respects_target_width() {
let mut params = Params::new();
params.insert("u32_max".to_string(), json!(u32::MAX));
params.insert("u32_overflow".to_string(), json!(4_294_967_296_u64));
assert_eq!(params.get_usize("u32_max"), Some(4_294_967_295_usize));
#[cfg(target_pointer_width = "32")]
assert_eq!(params.get_usize("u32_overflow"), None);
#[cfg(target_pointer_width = "64")]
assert_eq!(params.get_usize("u32_overflow"), Some(4_294_967_296));
}
}