extism_convert/
from_bytes.rs

1use crate::*;
2
3pub use extism_convert_macros::FromBytes;
4
5/// `FromBytes` is used to define how a type should be decoded when working with
6/// Extism memory. It is used for plugin output and host function input.
7///
8/// `FromBytes` can be derived by delegating encoding to generic type implementing
9/// `FromBytes`, e.g., [`Json`], [`Msgpack`].
10///
11/// ```
12/// use extism_convert::{Json, FromBytes};
13/// use serde::Deserialize;
14///
15/// #[derive(FromBytes, Deserialize, PartialEq, Debug)]
16/// #[encoding(Json)]
17/// struct Struct {
18///     hello: String,
19/// }
20///
21/// assert_eq!(Struct::from_bytes(br#"{"hello":"hi"}"#)?, Struct { hello: "hi".into() });
22/// # Ok::<(), extism_convert::Error>(())
23/// ```
24///
25/// Custom encodings can also be used, through new-types with a single generic
26/// argument, i.e., `Type<T>(T)`, that implement `FromBytesOwned` for the struct.
27///
28/// ```
29/// use std::str::{self, FromStr};
30/// use std::convert::Infallible;
31/// use extism_convert::{Error, FromBytes, FromBytesOwned};
32///
33/// // Custom serialization using `FromStr`
34/// struct StringEnc<T>(T);
35/// impl<T: FromStr> FromBytesOwned for StringEnc<T> where Error: From<<T as FromStr>::Err> {
36///     fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
37///         Ok(Self(str::from_utf8(data)?.parse()?))
38///     }
39/// }
40///
41/// #[derive(FromBytes, PartialEq, Debug)]
42/// #[encoding(StringEnc)]
43/// struct Struct {
44///     hello: String,
45/// }
46///
47/// impl FromStr for Struct {
48///     type Err = Infallible;
49///     fn from_str(s: &str) -> Result<Self, Infallible> {
50///         Ok(Self { hello: s.to_owned() })
51///     }
52/// }
53///
54/// assert_eq!(Struct::from_bytes(b"hi")?, Struct { hello: "hi".into() });
55/// # Ok::<(), extism_convert::Error>(())
56/// ```
57pub trait FromBytes<'a>: Sized {
58    /// Decode a value from a slice of bytes
59    fn from_bytes(data: &'a [u8]) -> Result<Self, Error>;
60}
61
62/// `FromBytesOwned` is similar to [`FromBytes`] but it doesn't borrow from the input slice.
63/// [`FromBytes`] is automatically implemented for all types that implement `FromBytesOwned`.
64///
65/// `FromBytesOwned` can be derived through [`#[derive(FromBytes)]`](FromBytes).
66pub trait FromBytesOwned: Sized {
67    /// Decode a value from a slice of bytes, the resulting value should not borrow the input
68    /// data.
69    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error>;
70}
71
72impl<'a> FromBytes<'a> for &'a [u8] {
73    fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
74        Ok(data)
75    }
76}
77
78impl<'a> FromBytes<'a> for &'a str {
79    fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
80        Ok(std::str::from_utf8(data)?)
81    }
82}
83
84impl<'a, T: FromBytesOwned> FromBytes<'a> for T {
85    fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
86        T::from_bytes_owned(data)
87    }
88}
89
90impl FromBytesOwned for Box<[u8]> {
91    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
92        Ok(data.to_vec().into_boxed_slice())
93    }
94}
95
96impl FromBytesOwned for Vec<u8> {
97    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
98        Ok(data.to_vec())
99    }
100}
101
102impl FromBytesOwned for String {
103    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
104        Ok(std::str::from_utf8(data)?.to_string())
105    }
106}
107
108impl FromBytesOwned for f64 {
109    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
110        Ok(Self::from_le_bytes(data.try_into()?))
111    }
112}
113
114impl FromBytesOwned for f32 {
115    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
116        Ok(Self::from_le_bytes(data.try_into()?))
117    }
118}
119
120impl FromBytesOwned for i64 {
121    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
122        Ok(Self::from_le_bytes(data.try_into()?))
123    }
124}
125
126impl FromBytesOwned for i32 {
127    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
128        Ok(Self::from_le_bytes(data.try_into()?))
129    }
130}
131
132impl FromBytesOwned for u64 {
133    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
134        Ok(Self::from_le_bytes(data.try_into()?))
135    }
136}
137
138impl FromBytesOwned for u32 {
139    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
140        Ok(Self::from_le_bytes(data.try_into()?))
141    }
142}
143
144impl FromBytesOwned for bool {
145    fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
146        if let Some(x) = data.first() {
147            Ok(*x != 0)
148        } else {
149            Err(Error::msg("Expected one byte to read boolean value"))
150        }
151    }
152}
153
154impl FromBytesOwned for () {
155    fn from_bytes_owned(_: &[u8]) -> Result<Self, Error> {
156        Ok(())
157    }
158}
159
160impl<'a, T: FromBytes<'a>> FromBytes<'a> for std::io::Cursor<T> {
161    fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
162        Ok(std::io::Cursor::new(T::from_bytes(data)?))
163    }
164}
165
166impl<'a, T: FromBytes<'a>> FromBytes<'a> for Option<T> {
167    fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
168        if data.is_empty() {
169            return Ok(None);
170        }
171
172        T::from_bytes(data).map(Some)
173    }
174}