bp3d_proto/message/
interface.rs

1// Copyright (c) 2025, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above copyright notice,
11//       this list of conditions and the following disclaimer in the documentation
12//       and/or other materials provided with the distribution.
13//     * Neither the name of BlockProject 3D nor the names of its contributors
14//       may be used to endorse or promote products derived from this software
15//       without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29use bp3d_util::simple_error;
30
31simple_error! {
32    pub Error {
33        InvalidUtf8 => "invalid UTF-8 string",
34        Truncated => "truncated input",
35        InvalidUnionDiscriminant(usize) => "invalid union discriminant ({})",
36        (impl From) Io(std::io::Error) => "io error: {}"
37    }
38}
39
40pub type Result<T> = std::result::Result<T, Error>;
41
42#[derive(Default, Copy, Clone, Debug)]
43pub struct FieldOffset {
44    pub start: usize,
45    pub end: usize,
46}
47
48impl FieldOffset {
49    pub fn size(&self) -> usize {
50        self.end - self.start
51    }
52}
53
54pub struct Message<T> {
55    data: T,
56    size: usize,
57}
58
59impl<T> Message<T> {
60    pub fn new(size: usize, data: T) -> Self {
61        Self { data, size }
62    }
63
64    pub fn into_inner(self) -> T {
65        self.data
66    }
67
68    /// Access the underlying parts of this message.
69    pub fn into_parts(self) -> (T, usize) {
70        (self.data, self.size)
71    }
72
73    pub fn size(&self) -> usize {
74        self.size
75    }
76
77    pub fn map<T1, F: FnOnce(T) -> T1>(self, f: F) -> Message<T1> {
78        Message::new(self.size(), f(self.into_inner()))
79    }
80}
81
82pub trait FromBytes<'a> {
83    type Output: Sized;
84
85    fn from_bytes(slice: &'a [u8]) -> Result<Message<Self::Output>>;
86    //fn copy_to_slice(&self, out_slice: &mut [u8]);
87}
88
89pub trait FromBytesWithOffsets<'a>: FromBytes<'a> {
90    type Offsets: Sized;
91
92    fn from_bytes_with_offsets(slice: &'a [u8]) -> Result<Message<(Self::Output, Self::Offsets)>>;
93}
94
95pub trait WriteTo {
96    type Input<'a>: Sized;
97
98    fn write_to<W: std::io::Write>(input: &Self::Input<'_>, out: W) -> Result<()>;
99}
100
101pub trait ShapeAndWrite {
102    fn shape_and_write<W: std::io::Write>(self, out: W) -> Result<()>;
103}
104
105#[cfg(feature = "tokio")]
106pub trait ShapeAndWriteAsync {
107    fn shape_and_write_async<W: tokio::io::AsyncWriteExt + Unpin>(
108        self,
109        out: W,
110    ) -> impl std::future::Future<Output = Result<()>>;
111}
112
113#[cfg(feature = "tokio")]
114pub trait WriteToAsync: WriteTo {
115    fn write_to_async<W: tokio::io::AsyncWriteExt + Unpin>(
116        input: &Self::Input<'_>,
117        out: W,
118    ) -> impl std::future::Future<Output = Result<()>>;
119}
120
121pub trait WriteSelf {
122    fn write_self<W: std::io::Write>(&self, out: W) -> Result<()>;
123    fn size(&self) -> Result<usize>;
124}
125
126#[cfg(feature = "tokio")]
127pub trait WriteSelfAsync {
128    fn write_self_async<W: tokio::io::AsyncWriteExt + Unpin>(
129        &self,
130        out: W,
131    ) -> impl std::future::Future<Output = Result<()>>;
132}
133
134pub trait FromBytesWithHeader<'a, H> {
135    type Output: Sized;
136
137    fn from_bytes_with_header(slice: &'a [u8], header: &H) -> Result<Message<Self::Output>>;
138}
139
140pub trait WriteToWithHeader<H> {
141    type Input<'a>: Sized;
142
143    fn write_to_with_header<W: std::io::Write>(input: &Self::Input<'_>, header: &H, out: W) -> Result<()>;
144}
145
146pub trait ShapeHeader<H> {
147    fn shape_header(&self, header: &mut H) -> Result<()>;
148}
149
150impl<H, T: WriteSelf> ShapeHeader<H> for T {
151    fn shape_header(&self, _: &mut H) -> Result<()> {
152        Ok(())
153    }
154}
155
156#[cfg(feature = "tokio")]
157pub trait WriteToWithHeaderAsync<H>: WriteToWithHeader<H> {
158    fn write_to_with_header_async<W: tokio::io::AsyncWriteExt + Unpin>(
159        input: &Self::Input<'_>,
160        header: &H,
161        out: W,
162    ) -> impl std::future::Future<Output = Result<()>>;
163}
164
165impl<'a, T: WriteTo<Input<'a> = T>> WriteSelf for T {
166    fn write_self<W: std::io::Write>(&self, out: W) -> Result<()> {
167        T::write_to(self, out)
168    }
169
170    fn size(&self) -> Result<usize> {
171        crate::message::util::size_of(self)
172    }
173}
174
175#[cfg(feature = "tokio")]
176impl<T> WriteSelfAsync for T
177where
178    for<'a> T: WriteToAsync<Input<'a> = T>,
179{
180    async fn write_self_async<W: tokio::io::AsyncWriteExt + Unpin>(&self, out: W) -> Result<()> {
181        T::write_to_async(self, out).await
182    }
183}
184
185impl<H, T: WriteSelf> WriteToWithHeader<H> for T {
186    type Input<'b> = T;
187
188    fn write_to_with_header<W: std::io::Write>(input: &Self::Input<'_>, _: &H, out: W) -> Result<()> {
189        input.write_self(out)
190    }
191}
192
193#[cfg(feature = "tokio")]
194impl<H, T: WriteSelf + WriteSelfAsync> WriteToWithHeaderAsync<H> for T {
195    fn write_to_with_header_async<W: tokio::io::AsyncWriteExt + Unpin>(
196        input: &Self::Input<'_>,
197        _: &H,
198        out: W,
199    ) -> impl std::future::Future<Output = Result<()>> {
200        input.write_self_async(out)
201    }
202}
203
204impl<'a, H, T: FromBytes<'a>> FromBytesWithHeader<'a, H> for T {
205    type Output = T::Output;
206
207    fn from_bytes_with_header(slice: &'a [u8], _: &H) -> Result<Message<Self::Output>> {
208        T::from_bytes(slice)
209    }
210}