1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Buffa wire ⇄ domain conversion layer.
//!
//! Gated on `feature = "buffa"`. The hand-written domain types live in
//! `crate::domain::*`; the buffa-generated wire types live in
//! `crate::generated::media::v1::*`. This module provides the
//! `From<&Wire> for Domain` (fallibly, via `TryFrom` where the domain
//! rejects values the wire layer can carry) and
//! `From<&Domain> for Wire` (always infallible) bridge between them.
//!
//! ## Coverage
//!
//! ### Bridged (round-trip tested):
//!
//! - **Primitives** — [`primitives`]:
//! - `media.v1::Id` ⇄ `domain::Uuid7`
//! - `media.v1::FileChecksum` ⇄ `domain::FileChecksum`
//! - `media.v1::ErrorInfo` ⇄ `domain::ErrorInfo` (+ `ErrorCode`)
//! - **Location oneof** — [`location`]:
//! - `media.v1::Local` ⇄ `domain::Location::Local`
//! - `media.v1::Location` ⇄ `Option<domain::Location>`
//! - **Enums** — [`enums`]:
//! - `media.v1::DbMediaKind` ⇄ `domain::MediaKind`
//! - `buffa::EnumValue<DbMediaKind>` ⇄ `domain::MediaKind`
//! - **Aggregates**:
//! - [`watched_location`] — `media.v1::WatchedLocation` ⇄
//! `domain::WatchedLocation` (partial: see module doc).
//! - [`media`] — `media.v1::Media` ⇄ `domain::Media` (partial: see
//! module doc).
//! - [`media_file`] — `media.v1::MediaFile` ⇄ `domain::MediaFile`
//! (1:1 — wire shape mirrors the domain, including `watch_volume`,
//! so a single message round-trips losslessly).
//! - [`person`] — `media.v1::Person` ⇄ `domain::Person` (1:1 with
//! the locked schema; `voiceprint` embedded as
//! `optional VoiceFingerprint`).
//! - [`speaker`] — `media.v1::Speaker` ⇄ `domain::Speaker` (1:1
//! with the locked schema; `voiceprint` + `person` FK additive
//! fields included).
//! - [`audio_segment`] — `media.v1::AudioSegment` ⇄
//! `domain::AudioSegment` (1:1 with the locked schema; nested
//! `Word` list + `LocalizedText` + `Language` +
//! `voice_fingerprint` all bridged).
//! - [`subtitle`] — polymorphic `media.v1::SubtitleCue` ⇄
//! `domain::SubtitleCue<Uuid7, SubtitleCueDetails<Uuid7>>` (with
//! per-format `SrtCue` / `VttCue` / `AssCue` / `LrcCue` infallible
//! encoders), plus the per-track aggregates `VttRegion`,
//! `VttStyleBlock`, `AssStyle`, `LrcMetadata`, and the LRC child
//! `LrcWord`. Wire timebase is dedup'd onto the parent
//! `SubtitleTrack`; decode takes a `parent_timebase` argument.
//! - **Cross-cutting VOs**:
//! - [`voice_fingerprint`] — `media.v1::VoiceFingerprint` ⇄
//! `domain::VoiceFingerprint` and `media.v1::Provenance` ⇄
//! `domain::Provenance`. Embedded VOs; the helpers
//! `voice_fingerprint_to_wire` / `voice_fingerprint_from_wire`
//! are reused by every parent that holds an
//! `optional VoiceFingerprint` slot (`Person.voiceprint`,
//! `Speaker.voiceprint`, `AudioSegment.voice_fingerprint`).
//! - [`audio_segment`] also factors module-private
//! `LocalizedText` ⇄ `wire::LocalizedText` and `Language` ⇄
//! `wire::Language` helpers (the only current parents are inside
//! the audio cluster; promoted to a shared module the next time a
//! non-audio parent embeds one).
//!
//! ### Not yet bridged (no clean wire counterpart)
//!
//! The buffa-generated wire layer in this crate predates the
//! `0.1.0`-locked schema redesign and uses a different field set for
//! the deeper aggregates. The following domain aggregates have **no
//! structural counterpart** in `media.v1` and are tracked as a
//! follow-up once the wire layer is regenerated against the locked
//! `schema/*.md` docs:
//!
//! - `Video` / `VideoTrack` / `Scene` / `Keyframe` — wire `Video` /
//! `VideoTrack` / `Scene` / `Keyframe` exist but carry an entirely
//! different field set (per-track metadata wrapped in `*Meta`
//! messages, plus FFmpeg-shaped detection structs that don't
//! correspond to any domain type).
//! - `Audio` / `AudioTrack` — the wire `Audio` wraps an
//! `AudioMeta`/`AudioStreamMeta`/`AudioSummary` tree that doesn't
//! match the locked aggregates. `AudioSegment` is bridged
//! independently above against its own freshly-added wire message.
//! - `Subtitle` / `SubtitleTrack` — wire `Subtitle` / `SubtitleTrack`
//! carry pre-locked-schema fields; only the polymorphic `SubtitleCue`
//! redesign is bridged (above).
//! - `UserTag`, `SceneAnnotation`, `IndexProgress` — no wire
//! counterpart at all (or a fundamentally different shape).
//! - The capture VOs are now the published mediaframe types
//! (`mediaframe::capture::Device` / `GeoLocation`), bridged inline on
//! wire `Media`: `Device` ⇄ `device_make`/`device_model` pair, and
//! `GeoLocation` ⇄ the ISO 6709 `gps_location` string (round-tripped
//! via `from_iso6709`/`to_iso6709`).
//!
//! ## Error model
//!
//! All wire → domain failures surface as [`BuffaError`] (see [`error`]).
//! Variants carry the lower-level domain validating error (`Uuid7Error`,
//! `LocationError`) verbatim so callers can recover via `is_*` /
//! `try_unwrap_*` predicates.
pub use BuffaError;