use jiff::Timestamp as JiffTimestamp;
use smol_str::SmolStr;
use crate::{
buffa::{
error::BuffaError,
voice_fingerprint::{voice_fingerprint_from_wire, voice_fingerprint_to_wire},
},
domain::{
aggregates::person::{Person, PersonConfidence},
Uuid7,
},
generated::media::v1 as wire,
};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
#[allow(unused_imports)]
use std::{
borrow::ToOwned,
string::{String, ToString},
};
impl From<PersonConfidence> for wire::PersonConfidence {
fn from(d: PersonConfidence) -> Self {
match d {
PersonConfidence::AutoMatched => wire::PersonConfidence::PERSON_CONFIDENCE_AUTO_MATCHED,
PersonConfidence::UserConfirmed => wire::PersonConfidence::PERSON_CONFIDENCE_USER_CONFIRMED,
}
}
}
impl From<wire::PersonConfidence> for PersonConfidence {
fn from(w: wire::PersonConfidence) -> Self {
match w {
wire::PersonConfidence::PERSON_CONFIDENCE_AUTO_MATCHED => PersonConfidence::AutoMatched,
wire::PersonConfidence::PERSON_CONFIDENCE_USER_CONFIRMED => PersonConfidence::UserConfirmed,
}
}
}
impl From<PersonConfidence> for ::buffa::EnumValue<wire::PersonConfidence> {
fn from(d: PersonConfidence) -> Self {
::buffa::EnumValue::Known(wire::PersonConfidence::from(d))
}
}
impl TryFrom<&::buffa::EnumValue<wire::PersonConfidence>> for PersonConfidence {
type Error = BuffaError;
fn try_from(w: &::buffa::EnumValue<wire::PersonConfidence>) -> Result<Self, Self::Error> {
match w {
::buffa::EnumValue::Known(k) => Ok(PersonConfidence::from(*k)),
::buffa::EnumValue::Unknown(v) => Err(BuffaError::UnknownEnumValue(*v)),
}
}
}
impl From<&Person<Uuid7>> for wire::Person {
fn from(d: &Person<Uuid7>) -> Self {
wire::Person {
id: ::buffa::bytes::Bytes::copy_from_slice(d.id_ref().as_bytes()),
name: d.name().to_owned().into(),
confidence: d.confidence().into(),
voiceprint: voice_fingerprint_to_wire(d.voiceprint_ref()),
created_at: d.created_at().as_millisecond(),
updated_at: d.updated_at().as_millisecond(),
__buffa_unknown_fields: Default::default(),
}
}
}
impl TryFrom<&wire::Person> for Person<Uuid7> {
type Error = BuffaError;
fn try_from(w: &wire::Person) -> Result<Self, Self::Error> {
let id = id_from_bytes(&w.id)?;
let name = SmolStr::from(w.name.as_str());
let confidence = PersonConfidence::try_from(&w.confidence)?;
let voiceprint = voice_fingerprint_from_wire(&w.voiceprint)?;
let created_at = ms_to_jiff(w.created_at)?;
let updated_at = ms_to_jiff(w.updated_at)?;
Ok(Person::from_parts(
id, name, voiceprint, confidence, created_at, updated_at,
))
}
}
fn id_from_bytes(b: &::buffa::bytes::Bytes) -> Result<Uuid7, BuffaError> {
let arr: [u8; 16] = b
.as_ref()
.try_into()
.map_err(|_| BuffaError::IdWrongLength(b.len()))?;
Uuid7::try_from_bytes(arr).map_err(BuffaError::from)
}
fn ms_to_jiff(ms: i64) -> Result<JiffTimestamp, BuffaError> {
JiffTimestamp::from_millisecond(ms).map_err(|_| BuffaError::TimestampOutOfRange(ms))
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::domain::vo::{Provenance, VoiceFingerprint};
fn ts() -> JiffTimestamp {
JiffTimestamp::from_millisecond(1_700_000_000_000).expect("valid timestamp")
}
fn vfp() -> VoiceFingerprint<Uuid7> {
VoiceFingerprint::try_new(
Uuid7::new(),
192,
ts(),
Some(0.83),
Provenance::from_parts("ecapa-tdnn", "v1.0.0", "", "findit-indexer-0.1.0"),
)
.expect("valid voiceprint")
}
#[test]
fn person_confidence_roundtrip_auto_matched() {
let d = PersonConfidence::AutoMatched;
let w: wire::PersonConfidence = d.into();
assert_eq!(w, wire::PersonConfidence::PERSON_CONFIDENCE_AUTO_MATCHED);
let d2: PersonConfidence = w.into();
assert_eq!(d, d2);
}
#[test]
fn person_confidence_roundtrip_user_confirmed() {
let d = PersonConfidence::UserConfirmed;
let w: wire::PersonConfidence = d.into();
assert_eq!(w, wire::PersonConfidence::PERSON_CONFIDENCE_USER_CONFIRMED);
let d2: PersonConfidence = w.into();
assert_eq!(d, d2);
}
#[test]
fn person_confidence_enum_value_known_roundtrip() {
let ev: ::buffa::EnumValue<wire::PersonConfidence> = PersonConfidence::UserConfirmed.into();
assert!(ev.is_known());
let d = PersonConfidence::try_from(&ev).unwrap();
assert_eq!(d, PersonConfidence::UserConfirmed);
}
#[test]
fn person_confidence_enum_value_unknown_errors() {
let ev: ::buffa::EnumValue<wire::PersonConfidence> = ::buffa::EnumValue::Unknown(42);
let err = PersonConfidence::try_from(&ev).unwrap_err();
assert_eq!(err.try_unwrap_unknown_enum_value_ref(), Ok(&42));
}
#[test]
fn person_minimal_roundtrip() {
let p = Person::try_new(Uuid7::new(), "", ts(), ts()).expect("valid construction");
let w: wire::Person = (&p).into();
let p2: Person<Uuid7> = Person::try_from(&w).expect("roundtrip");
assert_eq!(p, p2);
assert!(p2.voiceprint_ref().is_none());
assert_eq!(p2.confidence(), PersonConfidence::AutoMatched);
}
#[test]
fn person_full_roundtrip_with_voiceprint() {
let p = Person::try_new(Uuid7::new(), "Jane Doe", ts(), ts())
.unwrap()
.with_voiceprint(vfp())
.with_confidence(PersonConfidence::UserConfirmed);
let w: wire::Person = (&p).into();
let p2: Person<Uuid7> = Person::try_from(&w).expect("roundtrip");
assert_eq!(p, p2);
}
#[test]
fn person_voiceprint_absence_round_trips_as_unset_message_field() {
let p = Person::try_new(Uuid7::new(), "", ts(), ts()).unwrap();
let w: wire::Person = (&p).into();
assert!(w.voiceprint.as_option().is_none());
let p2: Person<Uuid7> = Person::try_from(&w).unwrap();
assert!(p2.voiceprint_ref().is_none());
}
#[test]
fn person_wrong_length_id_errors() {
let p = Person::try_new(Uuid7::new(), "", ts(), ts()).unwrap();
let mut w: wire::Person = (&p).into();
w.id = ::buffa::bytes::Bytes::copy_from_slice(&[0u8; 8]);
let err = Person::try_from(&w).unwrap_err();
assert!(err.is_id_wrong_length());
}
#[test]
fn person_nil_id_errors() {
let p = Person::try_new(Uuid7::new(), "", ts(), ts()).unwrap();
let mut w: wire::Person = (&p).into();
w.id = ::buffa::bytes::Bytes::copy_from_slice(&[0u8; 16]);
let err = Person::try_from(&w).unwrap_err();
assert!(err.is_id_invalid());
}
#[test]
fn person_unknown_confidence_enum_errors() {
let p = Person::try_new(Uuid7::new(), "", ts(), ts()).unwrap();
let mut w: wire::Person = (&p).into();
w.confidence = ::buffa::EnumValue::Unknown(7);
let err = Person::try_from(&w).unwrap_err();
assert!(err.is_unknown_enum_value());
}
#[test]
fn person_created_at_out_of_range_errors() {
let p = Person::try_new(Uuid7::new(), "", ts(), ts()).unwrap();
let mut w: wire::Person = (&p).into();
w.created_at = i64::MAX;
let err = Person::try_from(&w).unwrap_err();
assert!(err.is_timestamp_out_of_range());
}
#[test]
fn person_shared_by_two_speakers_round_trip() {
let pid = Uuid7::new();
let p = Person::try_new(pid, "Jane", ts(), ts())
.unwrap()
.with_confidence(PersonConfidence::UserConfirmed)
.with_voiceprint(vfp());
let w: wire::Person = (&p).into();
let d1 = Person::try_from(&w).unwrap();
let d2 = Person::try_from(&w).unwrap();
assert_eq!(d1.id_ref(), &pid);
assert_eq!(d2.id_ref(), &pid);
assert_eq!(d1, d2);
assert_eq!(d1, p);
}
}