Skip to main content

sqlite_diff_rs/wire/
source.rs

1//! [`WireSource`]: sealed marker trait for CDC wire formats.
2
3use super::sealed::Sealed;
4use super::wire_type::WireType;
5
6/// Per-format marker naming a CDC wire source.
7///
8/// Implementors are unit structs owned by each format module
9/// (`PgWalstream`, `Wal2Json`, `Maxwell`). The associated payload type
10/// describes the format's per-column wire data.
11///
12/// Type identity is no longer source-native. Every payload carries a
13/// source-independent [`WireType`] that selects the decoder, so one
14/// semantic catalog drives every source without a per-source
15/// translation table.
16pub trait WireSource: Sealed {
17    /// Per-column payload the format hands to a decoder.
18    ///
19    /// Every payload struct carries a `column_name: &'a str` so decoder
20    /// errors are self-describing without an outer wrapping layer.
21    type Payload<'a>;
22
23    /// Semantic type of the column carried by the payload, used for
24    /// decoder dispatch.
25    fn wire_type(payload: &Self::Payload<'_>) -> WireType;
26
27    /// Extract the column name from a payload for diagnostic messages.
28    fn column_name<'a>(payload: &'a Self::Payload<'_>) -> &'a str;
29}
30
31/// Schema-side semantic type for one column of one table.
32pub trait WireColumnTypes {
33    /// Semantic [`WireType`] for the column at `column_index`.
34    fn column_type(&self, column_index: usize) -> WireType;
35}
36
37/// Table-name lookup for the [`DiffSetBuilder::digest`](crate::DiffSetBuilder::digest) entry point.
38pub trait WireSchema {
39    /// Concrete schema type for one table.
40    type Table: crate::schema::NamedColumns + WireColumnTypes;
41
42    /// Resolve a table name to its schema entry.
43    fn get(&self, table_name: &str) -> Option<&Self::Table>;
44}
45
46/// One CDC wire event digested via [`DiffSetBuilder::digest`](crate::DiffSetBuilder::digest).
47///
48/// Implemented in-crate for `pg_walstream::EventType`, `wal2json::MessageV2`,
49/// `wal2json::ChangeV1`, and `maxwell::Message` (each times both formats).
50pub trait Digestable<F, T, S, B>
51where
52    F: crate::builders::Format<S, B>,
53    T: crate::schema::NamedColumns + WireColumnTypes,
54{
55    /// Wire source this event came from.
56    type Src: WireSource;
57
58    /// Failure mode raised on schema lookup or decode failure.
59    type Error;
60
61    /// Fold this event into `builder`, resolving affected tables via `schema`
62    /// and decoding column payloads via `adapter`.
63    ///
64    /// # Errors
65    ///
66    /// Any per-source `ConversionError`.
67    fn digest_into<Sch, A>(
68        &self,
69        builder: crate::builders::DiffSetBuilder<F, T, S, B>,
70        schema: &Sch,
71        adapter: &A,
72    ) -> Result<crate::builders::DiffSetBuilder<F, T, S, B>, Self::Error>
73    where
74        Sch: WireSchema<Table = T>,
75        A: super::WireAdapter<Self::Src, S, B>;
76}
77
78/// A value read back from an executed Postgres query in binary result
79/// format, tagged with the catalog's semantic type for the column.
80///
81/// Unlike the CDC sources, the semantic type is not recoverable from the
82/// raw bytes (binary `int4` and `float4` are both four opaque bytes), so
83/// the payload carries the [`WireType`] explicitly, supplied by the
84/// caller from its catalog. This makes decoder dispatch deterministic and
85/// makes the caller choose the type the same way CDC does, which is what
86/// guarantees representation parity with the CDC paths.
87#[derive(Debug, Clone, Copy, Default)]
88pub struct PgBinary;
89
90impl Sealed for PgBinary {}
91
92impl WireSource for PgBinary {
93    type Payload<'a> = PgBinaryColumn<'a>;
94
95    fn wire_type(payload: &Self::Payload<'_>) -> WireType {
96        payload.wire_type
97    }
98
99    fn column_name<'a>(payload: &'a Self::Payload<'_>) -> &'a str {
100        payload.column_name
101    }
102}
103
104/// One binary result field for the [`PgBinary`] source.
105///
106/// `raw` is `None` for a SQL NULL. `wire_type` is the caller's catalog
107/// type for the column, not inferred from the bytes.
108#[derive(Debug, Clone, Copy)]
109pub struct PgBinaryColumn<'a> {
110    /// Column name, carried for self-describing decoder errors.
111    pub column_name: &'a str,
112    /// Semantic column type driving decoder dispatch.
113    pub wire_type: WireType,
114    /// Raw binary result bytes, or `None` for SQL NULL.
115    pub raw: Option<&'a [u8]>,
116}
117
118impl PgBinaryColumn<'_> {
119    /// Ergonomic helper for calling a specific [`Decoder`](super::Decoder)
120    /// on this payload without fully-qualified syntax. Fixes the `Src`
121    /// generic to [`PgBinary`] so the compiler can pick the impl.
122    ///
123    /// # Errors
124    ///
125    /// Propagates the decoder's [`DecodeError`](super::DecodeError).
126    pub fn decoded_by<D, S, B>(
127        self,
128        decoder: &D,
129    ) -> Result<crate::encoding::Value<S, B>, super::error::DecodeError>
130    where
131        D: super::decoder::Decoder<PgBinary, S, B>,
132    {
133        decoder.decode(self)
134    }
135}