tea-codec 0.2.0-dev.1

The TEA SDK
Documentation
use std::{any::TypeId, borrow::Cow};

use serde::{Deserialize, Serialize};
use smallvec::SmallVec;

use super::{DescriptableMark, Descriptor, Error, Global};

#[derive(Serialize, Deserialize)]
pub(crate) struct SerializedData<'a> {
	name: Cow<'a, str>,
	summary: Option<Cow<'a, str>>,
	detail: Option<Cow<'a, str>>,
	backtrace: Option<Cow<'a, str>>,
	pub(crate) inner: Option<SmallVec<[Cow<'a, Error>; 1]>>,
}

#[derive(Serialize, Deserialize)]
pub(crate) struct SerializedDataWithHuman<'a> {
	name: Cow<'a, str>,
	summary: Option<Cow<'a, str>>,
	detail: Option<Cow<'a, str>>,
	human: Option<String>,
	backtrace: Option<Cow<'a, str>>,
	pub(crate) inner: Option<SmallVec<[Cow<'a, Error>; 1]>>,
}

impl SerializedData<'_> {
	fn into_owned(self) -> SerializedData<'static> {
		SerializedData {
			name: self.name.into_owned().into(),
			summary: self.summary.map(|x| x.into_owned().into()),
			detail: self.detail.map(|x| x.into_owned().into()),
			backtrace: self.backtrace.map(|x| x.into_owned().into()),
			inner: self
				.inner
				.map(|x| x.into_iter().map(|x| Cow::Owned(x.into_owned())).collect()),
		}
	}
}

impl<'a> DescriptableMark<SerializedData<'a>> for Global {}

impl<'a> Descriptor<SerializedData<'a>> for Global {
	fn name<'x>(v: &'x SerializedData<'a>) -> Option<Cow<'x, str>> {
		Some(Cow::Borrowed(&v.name))
	}

	fn summary<'x>(v: &'x SerializedData<'a>) -> Option<Cow<'x, str>> {
		v.summary.as_deref().map(Cow::Borrowed)
	}

	fn detail<'x>(v: &'x SerializedData<'a>) -> Option<Cow<'x, str>> {
		v.detail.as_deref().map(Cow::Borrowed)
	}

	fn inner<'x>(v: &'x SerializedData<'a>) -> Option<smallvec::SmallVec<[&'x Error; 1]>> {
		v.inner
			.as_ref()
			.map(|x| x.iter().map(|x| x.as_ref()).collect())
	}

	fn type_id(_: &SerializedData) -> Option<std::any::TypeId> {
		Some(TypeId::of::<SerializedData>())
	}
}

impl<X> Serialize for Error<X> {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: serde::Serializer,
	{
		SerializeSpec::serialize(self, serializer)
	}
}

trait SerializeSpec<S>
where
	S: serde::Serializer,
{
	fn serialize(&self, serializer: S) -> Result<S::Ok, S::Error>;
}

impl<S, X> SerializeSpec<S> for Error<X>
where
	S: serde::Serializer,
{
	default fn serialize(&self, serializer: S) -> Result<S::Ok, S::Error> {
		self.to_serialized_data().serialize(serializer)
	}
}

impl<'a, X, W, F> SerializeSpec<&'a mut serde_json::Serializer<W, F>> for Error<X>
where
	&'a mut serde_json::Serializer<W, F>: serde::Serializer,
{
	fn serialize(
		&self,
		serializer: &'a mut serde_json::Serializer<W, F>,
	) -> Result<
		<&'a mut serde_json::Serializer<W, F> as serde::Serializer>::Ok,
		<&'a mut serde_json::Serializer<W, F> as serde::Serializer>::Error,
	> {
		self.to_serialized_data_with_human().serialize(serializer)
	}
}

impl<'a, X> Deserialize<'a> for Error<X> {
	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
	where
		D: serde::Deserializer<'a>,
	{
		DeserializeSpec::deserialize(deserializer)
	}
}

trait DeserializeSpec<'a, D>: Sized
where
	D: serde::Deserializer<'a>,
{
	fn deserialize(deserializer: D) -> Result<Self, D::Error>;
}

impl<'a, D, X> DeserializeSpec<'a, D> for Error<X>
where
	D: serde::Deserializer<'a>,
{
	default fn deserialize(deserializer: D) -> Result<Self, D::Error> {
		SerializedData::deserialize(deserializer)
			.map(|data| {
				#[cfg(feature = "backtrace")]
				let mut data = data;
				#[cfg(feature = "backtrace")]
				let backtrace = data.backtrace.take().unwrap();
				Error::<Global>::new(
					data,
					#[cfg(feature = "backtrace")]
					super::ErrorLocation::Serialized(backtrace),
				)
			})
			.map(Into::into)
	}
}

impl<'a, 'b, R, X> DeserializeSpec<'a, &'b mut serde_json::Deserializer<R>> for Error<X>
where
	&'b mut serde_json::Deserializer<R>: serde::Deserializer<'a>,
{
	fn deserialize(
		deserializer: &'b mut serde_json::Deserializer<R>,
	) -> Result<Self, <&'b mut serde_json::Deserializer<R> as serde::Deserializer<'a>>::Error> {
		SerializedDataWithHuman::deserialize(deserializer)
			.map(|data| {
				#[cfg(feature = "backtrace")]
				let mut data = data;
				#[cfg(feature = "backtrace")]
				let backtrace = data.backtrace.take().unwrap();
				Error::<Global>::new(
					SerializedData {
						name: data.name,
						summary: data.summary,
						detail: data.detail,
						backtrace: data.backtrace,
						inner: data.inner,
					},
					#[cfg(feature = "backtrace")]
					super::ErrorLocation::Serialized(backtrace),
				)
			})
			.map(Into::into)
	}
}

impl<S> Clone for Error<S> {
	fn clone(&self) -> Self {
		let result: Error<Global> = self.to_serialized_data().into_owned().into();
		result.into_scope()
	}
}

impl<S> Error<S> {
	fn to_serialized_data(&self) -> SerializedData {
		SerializedData {
			name: self.name(),
			summary: self.summary(),
			detail: self.detail(),
			#[cfg(feature = "backtrace")]
			backtrace: Some(self.backtrace()),
			#[cfg(not(feature = "backtrace"))]
			backtrace: None,
			inner: self
				.data
				.source
				.inner()
				.map(|inner| inner.into_iter().map(Cow::Borrowed).collect()),
		}
	}

	fn to_serialized_data_with_human(&self) -> SerializedDataWithHuman {
		SerializedDataWithHuman {
			name: self.name(),
			summary: self.summary(),
			detail: self.detail(),
			human: self.human(),
			#[cfg(feature = "backtrace")]
			backtrace: Some(self.backtrace()),
			#[cfg(not(feature = "backtrace"))]
			backtrace: None,
			inner: self
				.data
				.source
				.inner()
				.map(|inner| inner.into_iter().map(Cow::Borrowed).collect()),
		}
	}
}