149 lines
4.8 KiB
Rust
149 lines
4.8 KiB
Rust
use std::fmt::Display;
|
|
|
|
use chrono::DateTime;
|
|
use chrono_tz::Tz;
|
|
use ijson::IString;
|
|
use itertools::Itertools;
|
|
#[cfg(feature = "pyo3")]
|
|
use pyo3::{
|
|
exceptions::PyTypeError,
|
|
prelude::*,
|
|
types::{PyNone, PyTuple},
|
|
};
|
|
use snafu::{ResultExt, Snafu};
|
|
|
|
use super::arbitrary::{Arbitrary, MapKeyFromArbitraryError};
|
|
|
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum MapKey {
|
|
Null,
|
|
Bool(bool),
|
|
Integer(i64),
|
|
String(String),
|
|
Tuple(Vec<MapKey>),
|
|
DateTime(DateTime<Tz>),
|
|
}
|
|
|
|
impl Display for MapKey {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
MapKey::Null => write!(f, "null"),
|
|
MapKey::Bool(b) => write!(f, "{b}"),
|
|
MapKey::Integer(i) => write!(f, "{i}"),
|
|
MapKey::String(s) => write!(f, "{s}"),
|
|
MapKey::Tuple(vec) => {
|
|
let comma_separated =
|
|
Itertools::intersperse(vec.iter().map(ToString::to_string), ", ".to_string());
|
|
write!(f, "({})", String::from_iter(comma_separated))
|
|
}
|
|
MapKey::DateTime(date_time) => {
|
|
write!(f, "{}", date_time.to_rfc3339())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "pyo3")]
|
|
#[derive(Debug, Snafu)]
|
|
pub enum ExtractMapKeyError {
|
|
/// error getting the qualified type name when trying to report
|
|
/// that an instance of this type cannot be extracted as an [`Arbitrary`]
|
|
GetTypeNameError { source: PyErr },
|
|
|
|
/// error extracting the (successfully retrieved) fully qualified type name as a [`String`]
|
|
ExtractTypeNameError { source: PyErr },
|
|
|
|
/// can't extract a map key from a {type_name}
|
|
UnsupportedType { type_name: String },
|
|
}
|
|
|
|
#[cfg(feature = "pyo3")]
|
|
impl From<ExtractMapKeyError> for PyErr {
|
|
fn from(error: ExtractMapKeyError) -> Self {
|
|
use pyo3::exceptions::PyException;
|
|
|
|
match &error {
|
|
ExtractMapKeyError::GetTypeNameError { .. } => PyException::new_err(error.to_string()),
|
|
ExtractMapKeyError::ExtractTypeNameError { .. } => {
|
|
PyException::new_err(error.to_string())
|
|
}
|
|
ExtractMapKeyError::UnsupportedType { .. } => PyTypeError::new_err(error.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "pyo3")]
|
|
impl<'a, 'py> FromPyObject<'a, 'py> for MapKey {
|
|
type Error = ExtractMapKeyError;
|
|
|
|
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
|
|
if let Ok(_none) = ob.cast::<PyNone>() {
|
|
Ok(Self::Null)
|
|
} else if let Ok(b) = ob.extract() {
|
|
Ok(Self::Bool(b))
|
|
} else if let Ok(int) = ob.extract() {
|
|
Ok(Self::Integer(int))
|
|
} else if let Ok(s) = ob.extract() {
|
|
Ok(Self::String(s))
|
|
} else if let Ok(tuple) = ob.extract() {
|
|
Ok(Self::Tuple(tuple))
|
|
} else {
|
|
let type_name = ob
|
|
.get_type()
|
|
.fully_qualified_name()
|
|
.context(GetTypeNameSnafu)?
|
|
.extract()
|
|
.context(ExtractTypeNameSnafu)?;
|
|
Err(ExtractMapKeyError::UnsupportedType { type_name })
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "pyo3")]
|
|
impl<'py> IntoPyObject<'py> for MapKey {
|
|
type Target = PyAny;
|
|
|
|
type Output = Bound<'py, Self::Target>;
|
|
|
|
type Error = PyErr;
|
|
|
|
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
|
match self {
|
|
MapKey::Null => Ok(PyNone::get(py).to_owned().into_any()),
|
|
MapKey::Bool(b) => Ok(b.into_pyobject(py)?.to_owned().into_any()),
|
|
MapKey::Integer(i) => Ok(i.into_pyobject(py)?.into_any()),
|
|
MapKey::String(s) => Ok(s.into_pyobject(py)?.into_any()),
|
|
MapKey::Tuple(vec) => Ok(PyTuple::new(py, vec)?.into_any()),
|
|
MapKey::DateTime(date_time) => Ok(date_time.into_pyobject(py)?.into_any()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<MapKey> for IString {
|
|
fn from(map_key: MapKey) -> Self {
|
|
Self::from(map_key.to_string())
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Arbitrary> for MapKey {
|
|
type Error = MapKeyFromArbitraryError;
|
|
|
|
fn try_from(arbitrary: Arbitrary) -> Result<Self, Self::Error> {
|
|
match arbitrary {
|
|
Arbitrary::Null => Ok(MapKey::Null),
|
|
Arbitrary::Bool(b) => Ok(MapKey::Bool(b)),
|
|
Arbitrary::Integer(i) => Ok(MapKey::Integer(i)),
|
|
Arbitrary::String(s) => Ok(MapKey::String(s)),
|
|
Arbitrary::Array(vec) => {
|
|
let tuple = Result::from_iter(vec.into_iter().map(TryInto::try_into))?;
|
|
Ok(MapKey::Tuple(tuple))
|
|
}
|
|
Arbitrary::DateTime(date_time) => Ok(MapKey::DateTime(date_time)),
|
|
Arbitrary::Float(float) => {
|
|
Err(MapKeyFromArbitraryError::FloatNotSupported { value: float })
|
|
}
|
|
Arbitrary::Map(map) => Err(MapKeyFromArbitraryError::MapCannotBeAMapKey { value: map }),
|
|
}
|
|
}
|
|
}
|