Skip to main content

haem_frames/embedding/
codec.rs

1use std::fmt;
2use std::io::{Read, Write};
3use std::marker::PhantomData;
4use std::num::NonZeroU32;
5
6use serde::{
7    Deserialize, Deserializer, Serialize,
8    de::{DeserializeOwned, MapAccess, SeqAccess, Visitor, value::MapAccessDeserializer},
9};
10
11use super::{
12    Embed, Error, FAILED_MESSAGE_CHARS, Failed, FailedCode, MINIMUM_FRAME_BYTES, Ready, Start, Tag,
13    Vectors, check_ready, check_start,
14};
15
16struct Object<T>(T);
17
18impl<'de, T: Deserialize<'de>> Deserialize<'de> for Object<T> {
19    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
20        object(deserializer).map(Self)
21    }
22}
23
24pub(super) fn object<'de, D: Deserializer<'de>, T: Deserialize<'de>>(
25    deserializer: D,
26) -> Result<T, D::Error> {
27    struct ObjectVisitor<T>(PhantomData<T>);
28    impl<'de, T: Deserialize<'de>> Visitor<'de> for ObjectVisitor<T> {
29        type Value = T;
30        fn expecting(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
31            output.write_str("a JSON object")
32        }
33        fn visit_map<M: MapAccess<'de>>(self, map: M) -> Result<T, M::Error> {
34            T::deserialize(MapAccessDeserializer::new(map))
35        }
36    }
37    deserializer.deserialize_map(ObjectVisitor(PhantomData))
38}
39
40pub(super) fn objects<'de, D: Deserializer<'de>, T: Deserialize<'de>>(
41    deserializer: D,
42) -> Result<Vec<T>, D::Error> {
43    struct Objects<T>(PhantomData<T>);
44    impl<'de, T: Deserialize<'de>> Visitor<'de> for Objects<T> {
45        type Value = Vec<T>;
46        fn expecting(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
47            output.write_str("an array of JSON objects")
48        }
49        fn visit_seq<S: SeqAccess<'de>>(self, mut sequence: S) -> Result<Vec<T>, S::Error> {
50            let mut items = Vec::new();
51            while let Some(Object(item)) = sequence.next_element::<Object<T>>()? {
52                items.push(item);
53            }
54            Ok(items)
55        }
56    }
57    deserializer.deserialize_seq(Objects(PhantomData))
58}
59
60#[derive(Debug)]
61pub enum Message {
62    Start(Start),
63    Ready(Ready),
64    Embed(Embed),
65    Vectors(Vectors),
66    Failed(Failed),
67}
68
69impl Message {
70    #[must_use]
71    pub const fn tag(&self) -> Tag {
72        match self {
73            Self::Start(_) => Tag::Start,
74            Self::Ready(_) => Tag::Ready,
75            Self::Embed(_) => Tag::Embed,
76            Self::Vectors(_) => Tag::Vectors,
77            Self::Failed(_) => Tag::Failed,
78        }
79    }
80}
81
82fn protocol(error: impl fmt::Display) -> Error {
83    Error::new(FailedCode::EmbedProtocol, error.to_string())
84}
85
86fn parse<T: DeserializeOwned>(payload: &[u8]) -> Result<T, Error> {
87    let mut deserializer = serde_json::Deserializer::from_slice(payload);
88    let value = object(&mut deserializer).map_err(protocol)?;
89    deserializer.end().map_err(protocol)?;
90    Ok(value)
91}
92
93fn check_failed(failed: &Failed) -> Result<(), Error> {
94    if failed.message.chars().count() > FAILED_MESSAGE_CHARS {
95        return Err(protocol("Failed message exceeds 1024 characters"));
96    }
97    Ok(())
98}
99
100/// Decode a tagged JSON object and validate its exchange envelope.
101///
102/// # Errors
103/// Returns `embed_protocol` for unknown tags, malformed JSON, invalid Ready or
104/// Failed envelopes, and oversized Start messages; Start limits may refuse
105/// with `embed_limits`.
106pub fn decode_message(tag: u8, payload: &[u8]) -> Result<Message, Error> {
107    match Tag::try_from(tag)? {
108        Tag::Start => {
109            if payload
110                .len()
111                .checked_add(1)
112                .is_none_or(|size| size > MINIMUM_FRAME_BYTES as usize)
113            {
114                return Err(protocol("Start frame exceeds its fixed limit"));
115            }
116            let start = parse(payload)?;
117            check_start(&start)?;
118            Ok(Message::Start(start))
119        }
120        Tag::Ready => {
121            let ready = parse(payload)?;
122            check_ready(&ready)?;
123            Ok(Message::Ready(ready))
124        }
125        Tag::Embed => parse(payload).map(Message::Embed),
126        Tag::Vectors => parse(payload).map(Message::Vectors),
127        Tag::Failed => {
128            let failed = parse(payload)?;
129            check_failed(&failed)?;
130            Ok(Message::Failed(failed))
131        }
132    }
133}
134
135/// Read and decode one bounded frame; clean EOF returns no message.
136///
137/// # Errors
138/// Returns `embed_protocol` for framing or input failures; propagates the
139/// envelope and limit refusals from [`decode_message`].
140pub fn read_message(input: &mut impl Read, limit: NonZeroU32) -> Result<Option<Message>, Error> {
141    let Some(frame) = crate::frame::read(input, limit).map_err(protocol)? else {
142        return Ok(None);
143    };
144    let (tag, payload) = frame
145        .split_first()
146        .ok_or_else(|| protocol("frame has no tag"))?;
147    decode_message(*tag, payload).map(Some)
148}
149
150/// Read the first exchange with its fixed bound before allocating the payload.
151///
152/// # Errors
153/// Propagates framing and envelope refusals; returns `embed_protocol` when
154/// the first message is not Start.
155pub fn read_start(input: &mut impl Read) -> Result<Option<Start>, Error> {
156    let limit = NonZeroU32::new(MINIMUM_FRAME_BYTES)
157        .ok_or_else(|| protocol("fixed frame limit is zero"))?;
158    match read_message(input, limit)? {
159        Some(Message::Start(start)) => Ok(Some(start)),
160        None => Ok(None),
161        Some(_) => Err(protocol("first embedding frame must be Start")),
162    }
163}
164
165fn payload(value: &impl Serialize) -> Result<Vec<u8>, Error> {
166    serde_json::to_vec(value).map_err(protocol)
167}
168
169/// Validate an envelope and write its tagged JSON in one flushed frame.
170///
171/// # Errors
172/// Propagates Start and Ready validation refusals; returns `embed_protocol`
173/// for invalid Failed envelopes, encoding, framing, output or flush failures.
174pub fn write_message(
175    output: &mut impl Write,
176    message: &Message,
177    limit: NonZeroU32,
178) -> Result<(), Error> {
179    let bytes = match message {
180        Message::Start(start) => {
181            check_start(start)?;
182            payload(start)?
183        }
184        Message::Ready(ready) => {
185            check_ready(ready)?;
186            payload(ready)?
187        }
188        Message::Embed(embed) => payload(embed)?,
189        Message::Vectors(vectors) => payload(vectors)?,
190        Message::Failed(failed) => {
191            check_failed(failed)?;
192            payload(failed)?
193        }
194    };
195    let limit = if matches!(message, Message::Start(_)) {
196        limit.min(
197            NonZeroU32::new(MINIMUM_FRAME_BYTES)
198                .ok_or_else(|| protocol("fixed frame limit is zero"))?,
199        )
200    } else {
201        limit
202    };
203    crate::frame::write(output, message.tag() as u8, &bytes, limit).map_err(protocol)
204}