use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::fmt;
use std::marker::PhantomData;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::system_state::{StateError, SystemState};
use super::error::StorageError;
#[path = "json_payload_decoder/string.rs"]
mod string;
#[path = "json_payload_decoder/vec_f64.rs"]
mod vec_f64;
pub use string::JsonStringDecoder;
pub use vec_f64::JsonVecF64Decoder;
type BoxError = Box<dyn Error + Send + Sync + 'static>;
pub trait JsonPayloadDecoder<T>: Send + Sync + 'static {
type Error: Error + Send + Sync + 'static;
fn decode_json_payload(&self, raw_json: &str) -> Result<T, Self::Error>;
}
impl<T, E, F> JsonPayloadDecoder<T> for F
where
F: Fn(&str) -> Result<T, E> + Send + Sync + 'static,
E: Error + Send + Sync + 'static,
{
type Error = E;
fn decode_json_payload(&self, raw_json: &str) -> Result<T, Self::Error> {
self(raw_json)
}
}
#[derive(Default)]
pub struct JsonPayloadDecoderRegistry {
entries: HashMap<Box<str>, Box<dyn ErasedPayloadDecoder>>,
}
impl JsonPayloadDecoderRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
entries: HashMap::with_capacity(capacity),
}
}
pub fn with_json_field<T>(mut self, key: impl Into<String>) -> Result<Self, StorageError>
where
T: DeserializeOwned + Serialize + Clone + Send + 'static,
{
self.register_for_field::<T, _>(key, |raw_json: &str| serde_json::from_str::<T>(raw_json))?;
Ok(self)
}
pub fn register_for_field<T, D>(
&mut self,
key: impl Into<String>,
decoder: D,
) -> Result<(), StorageError>
where
T: Serialize + Clone + Send + 'static,
D: JsonPayloadDecoder<T>,
{
let key = key.into();
if key.is_empty() {
return Err(StorageError::InvalidConfiguration {
setting: "decoder.key",
reason: "decoder key must not be empty".to_owned(),
});
}
if self.entries.contains_key(key.as_str()) {
return Err(StorageError::DuplicateDecoder { field: key });
}
self.entries.insert(
key.into_boxed_str(),
Box::new(TypedDecoder {
decoder,
payload: PhantomData,
}),
);
Ok(())
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn has_decoder_for_field(&self, key: &str) -> bool {
self.entries.contains_key(key)
}
pub fn registered_field_names(&self) -> impl ExactSizeIterator<Item = &str> {
self.entries.keys().map(AsRef::as_ref)
}
pub(crate) fn require<'a>(
&self,
fields: impl IntoIterator<Item = &'a str>,
) -> Result<(), StorageError> {
let mut checked = HashSet::new();
for field in fields {
if checked.insert(field) && !self.has_decoder_for_field(field) {
return Err(StorageError::MissingDecoder {
field: field.to_owned(),
});
}
}
Ok(())
}
pub(crate) fn decode_into(
&self,
stream: &str,
iteration: u64,
field: &str,
raw_json: &str,
state: &mut SystemState,
) -> Result<(), StorageError> {
let decoder = self
.entries
.get(field)
.ok_or_else(|| StorageError::MissingDecoder {
field: field.to_owned(),
})?;
decoder
.decode_into(raw_json, field, state)
.map_err(|source| StorageError::DecodeField {
stream: stream.to_owned(),
iteration,
field: field.to_owned(),
source,
})
}
}
impl fmt::Debug for JsonPayloadDecoderRegistry {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut keys = self.registered_field_names().collect::<Vec<_>>();
keys.sort_unstable();
formatter
.debug_struct("JsonPayloadDecoderRegistry")
.field("keys", &keys)
.finish_non_exhaustive()
}
}
trait ErasedPayloadDecoder: Send + Sync {
fn decode_into(
&self,
raw_json: &str,
field: &str,
state: &mut SystemState,
) -> Result<(), BoxError>;
}
struct TypedDecoder<D, T> {
decoder: D,
payload: PhantomData<fn() -> T>,
}
impl<T, D> ErasedPayloadDecoder for TypedDecoder<D, T>
where
T: Serialize + Clone + Send + 'static,
D: JsonPayloadDecoder<T>,
{
fn decode_into(
&self,
raw_json: &str,
field: &str,
state: &mut SystemState,
) -> Result<(), BoxError> {
let payload = self
.decoder
.decode_json_payload(raw_json)
.map_err(|source| Box::new(source) as BoxError)?;
match state.insert_payload(field, payload) {
Ok(None) => Ok(()),
Ok(Some(previous)) => {
let decoded = state
.insert_payload(field, previous)
.expect("restoring an identical concrete payload type must succeed");
drop(decoded);
Err(Box::new(DecoderInsertError::Occupied {
field: field.to_owned(),
}))
}
Err(rejection) => {
let (source, payload) = rejection.into_parts();
drop(payload);
Err(Box::new(DecoderInsertError::State(source)))
}
}
}
}
#[derive(Debug)]
enum DecoderInsertError {
Occupied { field: String },
State(StateError),
}
impl fmt::Display for DecoderInsertError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Occupied { field } => {
write!(
formatter,
"decoded state field `{field}` is already populated"
)
}
Self::State(source) => source.fmt(formatter),
}
}
}
impl Error for DecoderInsertError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Occupied { .. } => None,
Self::State(source) => Some(source),
}
}
}