taskcluster_download/
factory.rs1use anyhow::Result;
2use async_trait::async_trait;
3use std::io::{Cursor, SeekFrom};
4use tokio::fs::File;
5use tokio::io::{AsyncSeekExt, AsyncWrite, AsyncWriteExt};
6
7#[async_trait]
11pub trait AsyncWriterFactory {
12 async fn get_writer<'a>(
21 &'a mut self,
22 content_length: Option<u64>,
23 ) -> Result<Box<dyn AsyncWrite + Unpin + 'a>>;
24}
25
26pub struct CursorWriterFactory<T>(Cursor<T>);
30
31#[async_trait]
32impl AsyncWriterFactory for CursorWriterFactory<Vec<u8>> {
33 async fn get_writer<'a>(
34 &'a mut self,
35 _: Option<u64>,
36 ) -> Result<Box<dyn AsyncWrite + Unpin + 'a>> {
37 self.0.get_mut().clear();
38 self.0.set_position(0);
39 Ok(Box::new(&mut self.0))
40 }
41}
42
43#[async_trait]
44impl AsyncWriterFactory for CursorWriterFactory<&mut [u8]> {
45 async fn get_writer<'a>(
46 &'a mut self,
47 _: Option<u64>,
48 ) -> Result<Box<dyn AsyncWrite + Unpin + 'a>> {
49 self.0.set_position(0);
50 Ok(Box::new(&mut self.0))
51 }
52}
53
54impl Default for CursorWriterFactory<Vec<u8>> {
55 fn default() -> Self {
56 Self(Cursor::new(Vec::new()))
57 }
58}
59
60impl CursorWriterFactory<Vec<u8>> {
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 pub fn into_inner(self) -> Vec<u8> {
67 self.0.into_inner()
68 }
69}
70
71impl<'a> CursorWriterFactory<&'a mut [u8]> {
72 pub fn for_buf(inner: &'a mut [u8]) -> Self {
73 Self(Cursor::new(inner))
74 }
75
76 pub fn size(self) -> usize {
79 self.0.position() as usize
80 }
81}
82
83pub struct FileWriterFactory(File);
87
88#[async_trait]
89impl AsyncWriterFactory for FileWriterFactory {
90 async fn get_writer<'a>(
91 &'a mut self,
92 _: Option<u64>,
93 ) -> Result<Box<dyn AsyncWrite + Unpin + 'a>> {
94 let mut file = self.0.try_clone().await?;
95 file.set_len(0).await?;
96 file.seek(SeekFrom::Start(0)).await?;
97 Ok(Box::new(file))
98 }
99}
100
101impl FileWriterFactory {
102 pub fn new(file: File) -> Self {
103 Self(file)
104 }
105
106 pub async fn into_inner(mut self) -> Result<File> {
109 self.0.flush().await?;
110 Ok(self.0)
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use super::*;
117 use anyhow::Result;
118 use tempfile::tempfile;
119 use tokio::io::{copy, AsyncReadExt, AsyncSeekExt};
120
121 const DATA: &[u8] = b"HELLO/WORLD";
122
123 async fn copy_to_factory<F: AsyncWriterFactory>(
124 data: &[u8],
125 factory: &mut F,
126 ) -> std::io::Result<()> {
127 let mut reader = Cursor::new(data);
128 let mut writer = factory.get_writer(Some(data.len() as u64)).await.unwrap();
129 copy(&mut reader, &mut writer).await?;
130 Ok(())
131 }
132
133 #[tokio::test]
134 async fn vec_cursor_writer_twice() -> Result<()> {
135 let mut factory = CursorWriterFactory::new();
136 copy_to_factory(b"wrong data, shouldn't see this", &mut factory).await?;
137 copy_to_factory(DATA, &mut factory).await?;
138 assert_eq!(&factory.into_inner(), DATA);
139 Ok(())
140 }
141
142 #[tokio::test]
143 async fn buf_cursor_writer_twice() -> Result<()> {
144 let mut buf = [0u8; 256];
145 let mut factory = CursorWriterFactory::for_buf(&mut buf[..]);
146 copy_to_factory(b"nobody should see this", &mut factory).await?;
147 copy_to_factory(DATA, &mut factory).await?;
148 let size = factory.size();
149 assert_eq!(&buf[..size], DATA);
150 Ok(())
151 }
152
153 #[tokio::test]
154 async fn buf_cursor_writer_too_small() -> Result<()> {
155 let mut buf = [0u8; 5];
156 let mut factory = CursorWriterFactory::for_buf(&mut buf[..]);
157 let err = copy_to_factory(DATA, &mut factory).await.unwrap_err();
158 assert_eq!(err.kind(), std::io::ErrorKind::WriteZero);
159 Ok(())
160 }
161
162 #[tokio::test]
163 async fn file_writer_twice() -> Result<()> {
164 let mut factory = FileWriterFactory::new(tempfile()?.into());
165 copy_to_factory(b"wrong data, shouldn't see this", &mut factory).await?;
166 copy_to_factory(DATA, &mut factory).await?;
167
168 let mut file = factory.into_inner().await?;
169 file.seek(SeekFrom::Start(0)).await?;
170
171 let mut res = Vec::new();
172 file.read_to_end(&mut res).await?;
173 assert_eq!(&res, DATA);
174 Ok(())
175 }
176}