use alloc::string::String;
use core::fmt;
use thiserror::Error;
use crate::{
core::buffer::InsertError,
errors::TransformError,
time::{TimePoint, Timestamp},
};
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum RegistryError<T = Timestamp>
where
T: TimePoint,
{
#[error("rotation is not a unit quaternion (norm: {0})")]
NonUnitRotation(f64),
#[error("transform contains non-finite values")]
NonFiniteValues,
#[error("a frame cannot be its own parent")]
SelfReferentialFrame,
#[error("re-parenting is not supported (the child frame's parent is {current_parent})")]
ReparentingNotSupported {
current_parent: String,
},
#[error("inserting the transform would create a cycle in the frame tree")]
CycleDetected,
#[error("cannot mix static and dynamic transforms for the same child frame")]
StaticDynamicConflict,
#[error("frame {0} does not exist in the transform tree")]
UnknownFrame(String),
#[error("no transform chain connects {target_frame} and {source_frame}")]
Disconnected {
target_frame: String,
source_frame: String,
},
#[error(
"transform from {source_frame} into {target_frame} at {} not found ({frame} {})",
.requested.as_seconds_lossy(),
Coverage(.covered)
)]
NotFoundAt {
target_frame: String,
source_frame: String,
frame: String,
requested: T,
covered: Option<(T, T)>,
},
#[error(
"no instant is covered by every hop between {target_frame} and {source_frame} ({frame} {})",
Coverage(.covered)
)]
NoCommonTime {
target_frame: String,
source_frame: String,
frame: String,
covered: Option<(T, T)>,
},
#[error("transform error: {0}")]
TransformError(#[source] TransformError),
}
impl<T> From<TransformError> for RegistryError<T>
where
T: TimePoint,
{
fn from(error: TransformError) -> Self {
match error {
TransformError::NonUnitRotation(norm) => Self::NonUnitRotation(norm),
TransformError::NonFiniteValues => Self::NonFiniteValues,
other => Self::TransformError(other),
}
}
}
impl<T> From<InsertError> for RegistryError<T>
where
T: TimePoint,
{
fn from(error: InsertError) -> Self {
match error {
InsertError::Invalid(error) => error.into(),
InsertError::StaticDynamicConflict => Self::StaticDynamicConflict,
InsertError::SelfReferentialFrame => Self::SelfReferentialFrame,
InsertError::ReparentingNotSupported(current_parent) => {
Self::ReparentingNotSupported { current_parent }
}
InsertError::ChildFrameMismatch { pinned, found } => {
Self::TransformError(TransformError::IncompatibleFrames {
expected: pinned,
found,
})
}
}
}
}
struct Coverage<'a, T>(&'a Option<(T, T)>);
impl<T> fmt::Display for Coverage<'_, T>
where
T: TimePoint,
{
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match self.0 {
Some((start, end)) => write!(
f,
"covers [{}, {}]",
start.as_seconds_lossy(),
end.as_seconds_lossy()
),
None => f.write_str("holds no transforms"),
}
}
}