use core::{fmt, str::FromStr};
use derive_more::{IsVariant, TryUnwrap, Unwrap};
#[cfg(any(feature = "std", feature = "alloc"))]
use smol_str::SmolStr;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Uuid7(Uuid);
#[derive(Debug, Clone, PartialEq, Eq, IsVariant, Unwrap, TryUnwrap, thiserror::Error)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
#[non_exhaustive]
pub enum Uuid7Error {
#[error("nil UUID is not a valid Uuid7 identity")]
Nil,
#[error("expected UUIDv7, got UUIDv{0}")]
WrongVersion(usize),
#[error("invalid UUID: {0}")]
InvalidUuid(#[from] uuid::Error),
}
fn validate_v7(u: Uuid) -> Result<Uuid7, Uuid7Error> {
if u.is_nil() {
return Err(Uuid7Error::Nil);
}
let v = u.get_version_num();
if v != 7 {
return Err(Uuid7Error::WrongVersion(v));
}
Ok(Uuid7(u))
}
#[cfg(feature = "std")]
impl Default for Uuid7 {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl Uuid7 {
#[cfg(feature = "std")]
#[inline(always)]
pub fn new() -> Self {
Self(Uuid::now_v7())
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) const fn nil() -> Self {
Self(Uuid::nil())
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn is_nil(&self) -> bool {
self.0.is_nil()
}
#[inline(always)]
pub const fn as_uuid(&self) -> Uuid {
self.0
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8; 16] {
self.0.as_bytes()
}
#[inline(always)]
pub fn try_from_bytes(bytes: [u8; 16]) -> Result<Self, Uuid7Error> {
validate_v7(Uuid::from_bytes(bytes))
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) const fn from_bytes_unchecked(bytes: [u8; 16]) -> Self {
Self(Uuid::from_bytes(bytes))
}
}
impl fmt::Display for Uuid7 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl FromStr for Uuid7 {
type Err = Uuid7Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let u = Uuid::parse_str(s)?;
validate_v7(u)
}
}
impl TryFrom<Uuid> for Uuid7 {
type Error = Uuid7Error;
fn try_from(u: Uuid) -> Result<Self, Self::Error> {
validate_v7(u)
}
}
impl From<Uuid7> for Uuid {
#[inline(always)]
fn from(id: Uuid7) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FileChecksum([u8; 32]);
impl FileChecksum {
#[inline(always)]
pub const fn new() -> Self {
Self([0; 32])
}
#[inline(always)]
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
#[inline(always)]
pub fn is_zero(&self) -> bool {
self.0 == [0; 32]
}
}
impl Default for FileChecksum {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for FileChecksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl From<[u8; 32]> for FileChecksum {
#[inline(always)]
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rgba(u32);
impl Rgba {
#[inline(always)]
pub const fn new() -> Self {
Self(0)
}
#[inline(always)]
pub const fn from_components(r: u8, g: u8, b: u8, a: u8) -> Self {
Self(((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | (a as u32))
}
#[inline(always)]
pub const fn from_bits(bits: u32) -> Self {
Self(bits)
}
#[inline(always)]
pub const fn bits(self) -> u32 {
self.0
}
#[inline(always)]
pub const fn r(self) -> u8 {
(self.0 >> 24) as u8
}
#[inline(always)]
pub const fn g(self) -> u8 {
(self.0 >> 16) as u8
}
#[inline(always)]
pub const fn b(self) -> u8 {
(self.0 >> 8) as u8
}
#[inline(always)]
pub const fn a(self) -> u8 {
self.0 as u8
}
}
impl Default for Rgba {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, IsVariant, thiserror::Error)]
#[non_exhaustive]
pub enum LocationError {
#[error("Location::Local requires a non-empty path")]
EmptyPath,
#[error("Location::Local requires a non-nil volume id")]
NilVolume,
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LocalLocation<Id = Uuid7> {
volume: Id,
components: std::vec::Vec<SmolStr>,
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<Id> LocalLocation<Id> {
#[inline(always)]
pub(crate) fn new<I, S>(volume: Id, components: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<SmolStr>,
{
Self {
volume,
components: components.into_iter().map(Into::into).collect(),
}
}
#[inline(always)]
pub fn volume_ref(&self) -> &Id {
&self.volume
}
#[inline(always)]
pub fn components_slice(&self) -> &[SmolStr] {
&self.components
}
#[inline(always)]
pub fn file_name(&self) -> &str {
self
.components
.last()
.map(SmolStr::as_str)
.unwrap_or_default()
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
#[non_exhaustive]
pub enum Location<Id = Uuid7> {
Local(LocalLocation<Id>),
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<Id> Location<Id> {
pub fn try_local<I, S>(volume: Id, components: I) -> Result<Self, LocationError>
where
I: IntoIterator<Item = S>,
S: Into<SmolStr>,
{
let components: std::vec::Vec<SmolStr> = components.into_iter().map(Into::into).collect();
if components.is_empty() {
return Err(LocationError::EmptyPath);
}
Ok(Self::Local(LocalLocation::new(volume, components)))
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl Location<Uuid7> {
pub fn try_local_uuid7<I, S>(volume: Uuid7, components: I) -> Result<Self, LocationError>
where
I: IntoIterator<Item = S>,
S: Into<SmolStr>,
{
if volume.is_nil() {
return Err(LocationError::NilVolume);
}
let components: std::vec::Vec<SmolStr> = components.into_iter().map(Into::into).collect();
if components.is_empty() {
return Err(LocationError::EmptyPath);
}
Ok(Self::Local(LocalLocation::new(volume, components)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnknownErrorCode(pub(crate) u32);
impl UnknownErrorCode {
#[inline(always)]
pub const fn get(self) -> u32 {
self.0
}
}
impl fmt::Display for UnknownErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown({})", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
#[non_exhaustive]
pub enum ErrorCode {
BadRequest,
PermissionDenied,
NotFound,
AlreadyExists,
UnprocessableEntity,
InternalError,
ServiceUnavailable,
Timeout,
ProbeCorrupt,
ProbeUnsupportedFormat,
ProbeNoVideoStream,
ProbeNoAudioStream,
SceneDetectionFailed,
SceneDetectionModelError,
TranscriptionFailed,
TranscriptionModelError,
VlmFailed,
VlmModelError,
AppleVisionFailed,
AppleVisionRequestFailed,
EmbeddingFailed,
EmbeddingModelError,
EmbeddingModelLoadFailed,
EmbeddingPreprocessFailed,
EmbeddingInferenceFailed,
EmbeddingOutputInvalid,
PathNotFound,
VolumeNotAvailable,
MissingVolumeId,
MalformedVolumeId,
VolumeIdMismatch,
LocalDatabaseError,
FolderNotAvailable,
LocalPermissionDenied,
EndpointUnreachable,
AuthenticationFailed,
BucketNotFound,
QuotaExceeded,
RemoteDatabaseError,
RemoteTimeout,
Cancelled,
OutOfMemory,
CedFailed,
CedRequestFailed,
CedModelError,
Unknown(UnknownErrorCode),
}
impl ErrorCode {
pub const fn as_u32(self) -> u32 {
match self {
Self::BadRequest => 400,
Self::PermissionDenied => 403,
Self::NotFound => 404,
Self::AlreadyExists => 409,
Self::UnprocessableEntity => 422,
Self::InternalError => 500,
Self::ServiceUnavailable => 503,
Self::Timeout => 504,
Self::ProbeCorrupt => 1000,
Self::ProbeUnsupportedFormat => 1001,
Self::ProbeNoVideoStream => 1002,
Self::ProbeNoAudioStream => 1003,
Self::SceneDetectionFailed => 2000,
Self::SceneDetectionModelError => 2001,
Self::TranscriptionFailed => 3000,
Self::TranscriptionModelError => 3001,
Self::VlmFailed => 4000,
Self::VlmModelError => 4001,
Self::AppleVisionFailed => 5000,
Self::AppleVisionRequestFailed => 5001,
Self::EmbeddingFailed => 6000,
Self::EmbeddingModelError => 6001,
Self::EmbeddingModelLoadFailed => 6002,
Self::EmbeddingPreprocessFailed => 6003,
Self::EmbeddingInferenceFailed => 6004,
Self::EmbeddingOutputInvalid => 6005,
Self::PathNotFound => 7000,
Self::VolumeNotAvailable => 7001,
Self::MissingVolumeId => 7002,
Self::MalformedVolumeId => 7003,
Self::VolumeIdMismatch => 7004,
Self::LocalDatabaseError => 7005,
Self::FolderNotAvailable => 7006,
Self::LocalPermissionDenied => 7007,
Self::EndpointUnreachable => 8000,
Self::AuthenticationFailed => 8001,
Self::BucketNotFound => 8002,
Self::QuotaExceeded => 8003,
Self::RemoteDatabaseError => 8004,
Self::RemoteTimeout => 8005,
Self::Cancelled => 9000,
Self::OutOfMemory => 9001,
Self::CedFailed => 10000,
Self::CedRequestFailed => 10001,
Self::CedModelError => 10002,
Self::Unknown(u) => u.get(),
}
}
pub const fn from_u32(n: u32) -> Self {
match n {
400 => Self::BadRequest,
403 => Self::PermissionDenied,
404 => Self::NotFound,
409 => Self::AlreadyExists,
422 => Self::UnprocessableEntity,
500 => Self::InternalError,
503 => Self::ServiceUnavailable,
504 => Self::Timeout,
1000 => Self::ProbeCorrupt,
1001 => Self::ProbeUnsupportedFormat,
1002 => Self::ProbeNoVideoStream,
1003 => Self::ProbeNoAudioStream,
2000 => Self::SceneDetectionFailed,
2001 => Self::SceneDetectionModelError,
3000 => Self::TranscriptionFailed,
3001 => Self::TranscriptionModelError,
4000 => Self::VlmFailed,
4001 => Self::VlmModelError,
5000 => Self::AppleVisionFailed,
5001 => Self::AppleVisionRequestFailed,
6000 => Self::EmbeddingFailed,
6001 => Self::EmbeddingModelError,
6002 => Self::EmbeddingModelLoadFailed,
6003 => Self::EmbeddingPreprocessFailed,
6004 => Self::EmbeddingInferenceFailed,
6005 => Self::EmbeddingOutputInvalid,
7000 => Self::PathNotFound,
7001 => Self::VolumeNotAvailable,
7002 => Self::MissingVolumeId,
7003 => Self::MalformedVolumeId,
7004 => Self::VolumeIdMismatch,
7005 => Self::LocalDatabaseError,
7006 => Self::FolderNotAvailable,
7007 => Self::LocalPermissionDenied,
8000 => Self::EndpointUnreachable,
8001 => Self::AuthenticationFailed,
8002 => Self::BucketNotFound,
8003 => Self::QuotaExceeded,
8004 => Self::RemoteDatabaseError,
8005 => Self::RemoteTimeout,
9000 => Self::Cancelled,
9001 => Self::OutOfMemory,
10000 => Self::CedFailed,
10001 => Self::CedRequestFailed,
10002 => Self::CedModelError,
other => Self::Unknown(UnknownErrorCode(other)),
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ErrorInfo {
code: ErrorCode,
message: SmolStr,
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl ErrorInfo {
#[inline(always)]
pub fn new(code: ErrorCode, message: impl Into<SmolStr>) -> Self {
Self {
code,
message: message.into(),
}
}
#[inline(always)]
pub fn code_only(code: ErrorCode) -> Self {
Self {
code,
message: SmolStr::default(),
}
}
#[inline(always)]
pub const fn code(&self) -> ErrorCode {
self.code
}
#[inline(always)]
pub fn message(&self) -> &str {
self.message.as_str()
}
#[inline(always)]
#[must_use]
pub fn with_message(mut self, message: impl Into<SmolStr>) -> Self {
self.message = message.into();
self
}
#[inline(always)]
#[must_use]
pub fn with_code(mut self, code: ErrorCode) -> Self {
self.code = code;
self
}
#[inline(always)]
pub fn set_message(&mut self, message: impl Into<SmolStr>) -> &mut Self {
self.message = message.into();
self
}
#[inline(always)]
pub fn set_code(&mut self, code: ErrorCode) -> &mut Self {
self.code = code;
self
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn uuid7_new_is_non_nil_and_unique() {
let a = Uuid7::new();
let b = Uuid7::new();
assert!(!a.is_nil());
assert!(!b.is_nil());
assert_ne!(a, b, "two fresh UUIDv7s must differ");
}
#[test]
fn uuid7_nil_sentinel_must_be_explicit() {
let n = Uuid7::nil();
assert!(n.is_nil());
assert_eq!(n.as_bytes(), &[0; 16]);
}
#[test]
fn uuid7_string_roundtrip() {
let a = Uuid7::new();
let s = a.to_string();
let b: Uuid7 = s.parse().expect("parse roundtrip");
assert_eq!(a, b);
}
#[test]
fn uuid7_rejects_nil_via_validating_constructors() {
assert_eq!(Uuid7::try_from_bytes([0; 16]), Err(Uuid7Error::Nil));
assert_eq!(Uuid7::try_from(uuid::Uuid::nil()), Err(Uuid7Error::Nil));
assert_eq!(
"00000000-0000-0000-0000-000000000000".parse::<Uuid7>(),
Err(Uuid7Error::Nil)
);
assert!(Uuid7Error::Nil.is_nil());
assert!(!Uuid7Error::WrongVersion(4).is_nil());
}
#[test]
fn uuid7_rejects_non_v7_via_validating_constructors() {
let mut bytes = [0u8; 16];
bytes[0] = 0x01;
bytes[6] = 0x4a;
bytes[8] = 0x80;
let v4 = uuid::Uuid::from_bytes(bytes);
assert_eq!(v4.get_version_num(), 4);
let err = Uuid7::try_from(v4).unwrap_err();
assert!(err.is_wrong_version());
assert_eq!(err.try_unwrap_wrong_version_ref(), Ok(&4_usize));
let parse_err = v4.to_string().parse::<Uuid7>().unwrap_err();
assert_eq!(parse_err.try_unwrap_wrong_version_ref(), Ok(&4_usize));
}
#[test]
fn uuid7_invalid_string_returns_uuid_error() {
let err = "not-a-uuid".parse::<Uuid7>().unwrap_err();
assert!(err.is_invalid_uuid());
assert!(err.try_unwrap_invalid_uuid_ref().is_ok());
}
#[test]
fn uuid7_from_bytes_unchecked_still_works_for_wire_roundtrip() {
let a = Uuid7::new();
let raw = *a.as_bytes();
let b = Uuid7::from_bytes_unchecked(raw);
assert_eq!(a, b);
}
#[test]
fn uuid7_distinct_from_filechecksum_type() {
let _u: Uuid7 = Uuid7::nil();
let _c: FileChecksum = FileChecksum::new();
}
#[test]
fn filechecksum_hex_display() {
let bytes = [0xde, 0xad, 0xbe, 0xef]
.into_iter()
.cycle()
.take(32)
.collect::<Vec<u8>>()
.try_into()
.unwrap();
let cs = FileChecksum::from_bytes(bytes);
let s = cs.to_string();
assert_eq!(s.len(), 64);
assert!(s.starts_with("deadbeef"));
assert!(s.chars().all(|c| matches!(c, '0'..='9' | 'a'..='f')));
}
#[test]
fn filechecksum_new_is_zero_sentinel() {
let z = FileChecksum::new();
assert!(z.is_zero());
assert_eq!(z.to_string(), "0".repeat(64));
assert_eq!(FileChecksum::default(), z);
}
#[test]
fn rgba_pack_unpack() {
let c = Rgba::from_components(0x12, 0x34, 0x56, 0x78);
assert_eq!(c.bits(), 0x12_34_56_78);
assert_eq!(c.r(), 0x12);
assert_eq!(c.g(), 0x34);
assert_eq!(c.b(), 0x56);
assert_eq!(c.a(), 0x78);
assert_eq!(Rgba::from_bits(0x12_34_56_78), c);
}
#[test]
fn rgba_new_is_transparent_black() {
let n = Rgba::new();
assert_eq!(n.bits(), 0);
assert_eq!(Rgba::default(), n);
}
#[test]
fn location_try_local_uuid7_happy_path() {
let vol = Uuid7::new();
let l = Location::try_local_uuid7(vol, ["Movies", "Holiday"]).unwrap();
assert!(l.is_local());
let local = l.unwrap_local_ref();
assert_eq!(local.volume_ref(), &vol);
assert_eq!(local.components_slice(), &["Movies", "Holiday"]);
}
#[test]
fn local_location_file_name_is_last_component() {
let vol = Uuid7::new();
let l = Location::try_local_uuid7(vol, ["Movies", "2024", "Holiday.mp4"]).unwrap();
assert_eq!(l.unwrap_local_ref().file_name(), "Holiday.mp4");
let l = Location::try_local_uuid7(vol, ["loose.mkv"]).unwrap();
assert_eq!(l.unwrap_local_ref().file_name(), "loose.mkv");
}
#[test]
fn location_try_local_uuid7_rejects_nil_volume() {
let r = Location::try_local_uuid7(Uuid7::nil(), ["Movies"]);
assert_eq!(r, Err(LocationError::NilVolume));
assert!(LocationError::NilVolume.is_nil_volume());
}
#[test]
fn location_try_local_uuid7_rejects_empty_path() {
let vol = Uuid7::new();
let r = Location::try_local_uuid7::<core::iter::Empty<&str>, &str>(vol, core::iter::empty());
assert_eq!(r, Err(LocationError::EmptyPath));
assert!(LocationError::EmptyPath.is_empty_path());
}
#[test]
fn location_generic_try_local_rejects_empty_path() {
let r = Location::<u32>::try_local::<core::iter::Empty<&str>, &str>(7, core::iter::empty());
assert_eq!(r, Err(LocationError::EmptyPath));
}
#[test]
fn location_try_unwrap_local_returns_payload() {
let vol = Uuid7::new();
let l = Location::try_local_uuid7(vol, ["Movies"]).unwrap();
let local: LocalLocation<Uuid7> = l.try_unwrap_local().unwrap();
assert_eq!(local.volume_ref(), &vol);
assert_eq!(local.components_slice(), &["Movies"]);
}
#[test]
fn error_code_unknown_round_trips_wire_value() {
for wire in [42_u32, 1234, 99_999, u32::MAX] {
let c = ErrorCode::from_u32(wire);
assert!(c.is_unknown(), "{wire} should land in Unknown");
assert_eq!(c.as_u32(), wire, "wire->domain->wire must be identity");
let payload: UnknownErrorCode = c.unwrap_unknown();
assert_eq!(payload.get(), wire);
}
}
#[test]
fn error_code_from_u32_normalises_known_codes() {
let canonical = ErrorCode::from_u32(404);
assert_eq!(canonical, ErrorCode::NotFound);
assert!(!canonical.is_unknown());
assert!(canonical.try_unwrap_unknown_ref().is_err());
for c in [
ErrorCode::BadRequest,
ErrorCode::NotFound,
ErrorCode::ProbeCorrupt,
ErrorCode::from_u32(99_999), ] {
assert_eq!(ErrorCode::from_u32(c.as_u32()), c);
}
}
#[test]
fn error_code_known_round_trips_canonical_value() {
for c in [
ErrorCode::BadRequest,
ErrorCode::NotFound,
ErrorCode::InternalError,
ErrorCode::ProbeCorrupt,
ErrorCode::SceneDetectionFailed,
ErrorCode::TranscriptionFailed,
ErrorCode::EmbeddingOutputInvalid,
ErrorCode::LocalPermissionDenied,
ErrorCode::EndpointUnreachable,
ErrorCode::Cancelled,
ErrorCode::CedModelError,
] {
let n = c.as_u32();
let back = ErrorCode::from_u32(n);
assert_eq!(c, back, "{c:?} did not survive u32 round-trip");
assert!(!back.is_unknown());
}
}
#[test]
fn error_code_discriminants_match_findit_proto() {
assert_eq!(ErrorCode::BadRequest.as_u32(), 400);
assert_eq!(ErrorCode::NotFound.as_u32(), 404);
assert_eq!(ErrorCode::Timeout.as_u32(), 504);
assert_eq!(ErrorCode::ProbeCorrupt.as_u32(), 1000);
assert_eq!(ErrorCode::ProbeUnsupportedFormat.as_u32(), 1001);
assert_eq!(ErrorCode::SceneDetectionFailed.as_u32(), 2000);
assert_eq!(ErrorCode::TranscriptionFailed.as_u32(), 3000);
assert_eq!(ErrorCode::VlmFailed.as_u32(), 4000);
assert_eq!(ErrorCode::AppleVisionFailed.as_u32(), 5000);
assert_eq!(ErrorCode::EmbeddingFailed.as_u32(), 6000);
assert_eq!(ErrorCode::EmbeddingOutputInvalid.as_u32(), 6005);
assert_eq!(ErrorCode::PathNotFound.as_u32(), 7000);
assert_eq!(ErrorCode::LocalPermissionDenied.as_u32(), 7007);
assert_eq!(ErrorCode::EndpointUnreachable.as_u32(), 8000);
assert_eq!(ErrorCode::Cancelled.as_u32(), 9000);
assert_eq!(ErrorCode::OutOfMemory.as_u32(), 9001);
assert_eq!(ErrorCode::CedFailed.as_u32(), 10000);
assert_eq!(ErrorCode::CedModelError.as_u32(), 10002);
}
#[test]
fn error_info_construction_and_accessors() {
let e = ErrorInfo::new(ErrorCode::ProbeCorrupt, "bad container header");
assert_eq!(e.code(), ErrorCode::ProbeCorrupt);
assert_eq!(e.message(), "bad container header");
let e2 = ErrorInfo::code_only(ErrorCode::Cancelled);
assert_eq!(e2.code(), ErrorCode::Cancelled);
assert!(e2.message().is_empty());
let e3 = ErrorInfo::code_only(ErrorCode::ProbeCorrupt)
.with_message("clip.mp4")
.with_code(ErrorCode::ProbeUnsupportedFormat);
assert_eq!(e3.code(), ErrorCode::ProbeUnsupportedFormat);
assert_eq!(e3.message(), "clip.mp4");
let mut e4 = e.clone();
e4.set_message("");
e4.set_code(ErrorCode::Cancelled);
assert_eq!(e4.code(), ErrorCode::Cancelled);
assert!(e4.message().is_empty());
}
}