extism_convert/
to_bytes.rs

1use crate::*;
2
3pub use extism_convert_macros::ToBytes;
4
5/// `ToBytes` is used to define how a type should be encoded when working with
6/// Extism memory. It is used for plugin input and host function output.
7///
8/// `ToBytes` can be derived by delegating encoding to generic type implementing
9/// `ToBytes`, e.g., [`Json`], [`Msgpack`].
10///
11/// ```
12/// use extism_convert::{Json, ToBytes};
13/// use serde::Serialize;
14///
15/// #[derive(ToBytes, Serialize)]
16/// #[encoding(Json)]
17/// struct Struct {
18///     hello: String,
19/// }
20///
21/// assert_eq!(Struct { hello: "hi".into() }.to_bytes()?, br#"{"hello":"hi"}"#);
22/// # Ok::<(), extism_convert::Error>(())
23/// ```
24///
25/// But custom types can also be used, as long as they are new-types with a single
26/// generic argument, i.e., `Type<T>(T)`, that implement `ToBytes` for the struct.
27///
28/// ```
29/// use extism_convert::{Error, ToBytes};
30///
31/// // Custom serialization using `ToString`
32/// struct StringEnc<T>(T);
33/// impl<T: ToString> ToBytes<'_> for StringEnc<&T> {
34///     type Bytes = String;
35///     
36///     fn to_bytes(&self) -> Result<String, Error> {
37///         Ok(self.0.to_string())
38///     }
39/// }
40///
41/// #[derive(ToBytes)]
42/// #[encoding(StringEnc)]
43/// struct Struct {
44///     hello: String,
45/// }
46///
47/// impl ToString for Struct {
48///    fn to_string(&self) -> String {
49///        self.hello.clone()     
50///    }
51/// }
52///
53/// assert_eq!(Struct { hello: "hi".into() }.to_bytes()?, b"hi");
54/// # Ok::<(), Error>(())
55/// ```
56pub trait ToBytes<'a> {
57    /// A configurable byte slice representation, allows any type that implements `AsRef<[u8]>`
58    type Bytes: AsRef<[u8]>;
59
60    /// `to_bytes` converts a value into `Self::Bytes`
61    fn to_bytes(&self) -> Result<Self::Bytes, Error>;
62}
63
64impl ToBytes<'_> for () {
65    type Bytes = [u8; 0];
66    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
67        Ok([])
68    }
69}
70
71impl ToBytes<'_> for Vec<u8> {
72    type Bytes = Vec<u8>;
73    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
74        Ok(self.clone())
75    }
76}
77
78impl ToBytes<'_> for String {
79    type Bytes = String;
80    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
81        Ok(self.clone())
82    }
83}
84
85impl<'a> ToBytes<'a> for &'a [u8] {
86    type Bytes = &'a [u8];
87    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
88        Ok(self)
89    }
90}
91
92impl<'a> ToBytes<'a> for &'a str {
93    type Bytes = &'a str;
94    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
95        Ok(self)
96    }
97}
98
99impl ToBytes<'_> for f64 {
100    type Bytes = [u8; 8];
101
102    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
103        Ok(self.to_le_bytes())
104    }
105}
106
107impl ToBytes<'_> for f32 {
108    type Bytes = [u8; 4];
109
110    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
111        Ok(self.to_le_bytes())
112    }
113}
114
115impl ToBytes<'_> for i64 {
116    type Bytes = [u8; 8];
117
118    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
119        Ok(self.to_le_bytes())
120    }
121}
122
123impl ToBytes<'_> for i32 {
124    type Bytes = [u8; 4];
125
126    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
127        Ok(self.to_le_bytes())
128    }
129}
130
131impl ToBytes<'_> for u64 {
132    type Bytes = [u8; 8];
133
134    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
135        Ok(self.to_le_bytes())
136    }
137}
138
139impl ToBytes<'_> for u32 {
140    type Bytes = [u8; 4];
141
142    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
143        Ok(self.to_le_bytes())
144    }
145}
146
147impl<'a, T: ToBytes<'a>> ToBytes<'a> for &'a T {
148    type Bytes = T::Bytes;
149
150    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
151        <T as ToBytes>::to_bytes(self)
152    }
153}
154
155impl<'a, T: ToBytes<'a>> ToBytes<'a> for Option<T> {
156    type Bytes = Vec<u8>;
157
158    fn to_bytes(&self) -> Result<Self::Bytes, Error> {
159        match self {
160            Some(x) => x.to_bytes().map(|x| x.as_ref().to_vec()),
161            None => Ok(vec![]),
162        }
163    }
164}
165
166#[test]
167fn test() {
168    use extism_convert::{Json, ToBytes};
169    use serde::Serialize;
170
171    #[derive(ToBytes, Serialize)]
172    #[encoding(Json)]
173    struct Struct {
174        hello: String,
175    }
176}