1use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
2use futures_core::Stream;
3
4pub type Data = Vec<u8>;
8
9pub trait ImageGenerator {
14 type Error: core::error::Error + Send + Sync + 'static;
16
17 fn create(
28 &self,
29 prompt: Prompt,
30 size: Size,
31 ) -> impl Stream<Item = Result<Data, Self::Error>> + Send;
32
33 fn edit(
44 &self,
45 prompt: Prompt,
46 mask: &[u8],
47 ) -> impl Stream<Item = Result<Data, Self::Error>> + Send;
48}
49
50macro_rules! impl_image_generator {
51 ($($name:ident),*) => {
52 $(
53 impl<T: ImageGenerator> ImageGenerator for $name<T> {
54 type Error = T::Error;
55
56 fn create(
57 &self,
58 prompt: Prompt,
59 size: Size,
60 ) -> impl Stream<Item = Result<Data, Self::Error>> + Send {
61 T::create(self, prompt, size)
62 }
63
64 fn edit(
65 &self,
66 prompt: Prompt,
67 mask: &[u8],
68 ) -> impl Stream<Item = Result<Data, Self::Error>> + Send {
69 T::edit(self, prompt, mask)
70 }
71 }
72 )*
73 };
74}
75
76impl_image_generator!(Arc, Box);
77
78#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub struct Prompt {
82 text: String,
84 image: Vec<Data>,
86}
87
88impl Prompt {
89 #[must_use]
91 pub fn new(text: impl Into<String>) -> Self {
92 Self {
93 text: text.into(),
94 image: Vec::new(),
95 }
96 }
97
98 #[must_use]
100 pub fn text(&self) -> &str {
101 &self.text
102 }
103
104 #[must_use]
106 pub fn images(&self) -> &[Data] {
107 &self.image
108 }
109
110 #[must_use]
116 pub fn with_image(mut self, image: Data) -> Self {
117 self.image.push(image);
118 self
119 }
120}
121
122impl From<String> for Prompt {
123 fn from(text: String) -> Self {
129 Self::new(text)
130 }
131}
132
133impl From<&str> for Prompt {
134 fn from(text: &str) -> Self {
140 Self::new(text)
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147pub struct Size {
148 width: u32,
150 height: u32,
152}
153
154impl Size {
155 #[must_use]
162 pub const fn new(width: u32, height: u32) -> Self {
163 Self { width, height }
164 }
165
166 #[must_use]
172 pub const fn square(size: u32) -> Self {
173 Self {
174 width: size,
175 height: size,
176 }
177 }
178
179 #[must_use]
181 pub const fn width(&self) -> u32 {
182 self.width
183 }
184
185 #[must_use]
187 pub const fn height(&self) -> u32 {
188 self.height
189 }
190
191 #[must_use]
193 pub const fn pixel_count(&self) -> u64 {
194 self.width as u64 * self.height as u64
195 }
196
197 #[must_use]
199 pub const fn is_square(&self) -> bool {
200 self.width == self.height
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use core::convert::Infallible;
207
208 use super::*;
209 use alloc::vec;
210 use futures_lite::StreamExt;
211
212 struct MockImageGenerator;
213
214 impl ImageGenerator for MockImageGenerator {
215 type Error = Infallible;
216 fn create(
217 &self,
218 prompt: Prompt,
219 _size: Size,
220 ) -> impl Stream<Item = Result<Data, Self::Error>> + Send {
221 let prompt_bytes = prompt.text.as_bytes();
223 let chunk1 = prompt_bytes.to_vec();
224 let chunk2 = vec![0xFF, 0xD8, 0xFF, 0xE0]; let chunk3 = vec![0x00; 100]; futures_lite::stream::iter(vec![chunk1, chunk2, chunk3].into_iter().map(Ok))
228 }
229
230 fn edit(
231 &self,
232 prompt: Prompt,
233 _mask: &[u8],
234 ) -> impl Stream<Item = Result<Data, Self::Error>> + Send {
235 let prompt_bytes = prompt.text.as_bytes();
237 let chunk1 = prompt_bytes.to_vec();
238 let chunk2 = vec![0xFF, 0xD8, 0xFF, 0xE0]; let chunk3 = vec![0x00; 100]; futures_lite::stream::iter(vec![chunk1, chunk2, chunk3].into_iter().map(Ok))
242 }
243 }
244
245 #[tokio::test]
246 async fn image_generation() {
247 let generator = MockImageGenerator;
248 let mut stream = generator.create(Prompt::new("a cat"), Size::square(256));
249
250 let mut chunks = Vec::new();
251 while let Some(chunk) = stream.next().await {
252 chunks.push(chunk.unwrap());
253 }
254
255 assert_eq!(chunks.len(), 3);
256 assert_eq!(chunks[0], b"a cat".to_vec());
257 assert_eq!(chunks[1], vec![0xFF, 0xD8, 0xFF, 0xE0]);
258 assert_eq!(chunks[2], vec![0x00; 100]);
259 }
260
261 #[tokio::test]
262 async fn image_generation_empty_prompt() {
263 let generator = MockImageGenerator;
264 let mut stream = generator.create(Prompt::new(""), Size::square(256));
265
266 let mut chunks = Vec::new();
267 while let Some(chunk) = stream.next().await {
268 chunks.push(chunk.unwrap());
269 }
270
271 assert_eq!(chunks.len(), 3);
272 assert_eq!(chunks[0], b"".to_vec());
273 assert_eq!(chunks[1], vec![0xFF, 0xD8, 0xFF, 0xE0]);
274 assert_eq!(chunks[2], vec![0x00; 100]);
275 }
276
277 #[tokio::test]
278 async fn image_generation_long_prompt() {
279 let generator = MockImageGenerator;
280 let long_prompt = "a very detailed and elaborate description of a beautiful landscape with mountains, rivers, and forests";
281 let mut stream = generator.create(Prompt::new(long_prompt), Size::square(512));
282
283 let mut total_bytes = 0;
284 while let Some(chunk) = stream.next().await {
285 total_bytes += chunk.unwrap().len();
286 }
287
288 assert_eq!(total_bytes, long_prompt.len() + 4 + 100);
290 }
291
292 #[tokio::test]
293 async fn data_type_alias() {
294 let data: Data = vec![1, 2, 3, 4];
295 assert_eq!(data.len(), 4);
296 assert_eq!(data[0], 1);
297 assert_eq!(data[3], 4);
298 }
299
300 #[test]
301 fn data_operations() {
302 let mut data: Data = vec![0xFF; 1024];
303 assert_eq!(data.len(), 1024);
304
305 data.push(0x00);
306 assert_eq!(data.len(), 1025);
307 assert_eq!(data[1024], 0x00);
308
309 data.extend_from_slice(&[0x01, 0x02]);
310 assert_eq!(data.len(), 1027);
311 assert_eq!(data[1025], 0x01);
312 assert_eq!(data[1026], 0x02);
313 }
314}