Skip to main content

gapirs_common/api/client/
media.rs

1pub mod resumable;
2pub use download::*;
3pub use upload::*;
4pub(crate) mod multipart {
5    use super::{MediaUpload, MediaUploadStream};
6    use serde::{Deserialize, Serialize};
7    #[derive(Debug)]
8    pub struct MultipartBody {
9        pub(crate) boundary: String,
10        pub json_body: String,
11        pub media_body: MediaUpload<dyn MediaUploadStream>,
12        pub(crate) state: MultipartState,
13    }
14    impl MultipartBody {
15        pub fn new(
16            media_body: MediaUpload<dyn MediaUploadStream>,
17            body: impl Into<String>,
18        ) -> Self {
19            Self {
20                boundary: "BOUNDARY".to_string(),
21                json_body: body.into(),
22                media_body,
23                state: MultipartState::NotStarted,
24            }
25        }
26    }
27
28    #[derive(Debug, Clone)]
29    pub(crate) enum MultipartState {
30        NotStarted,
31        Polling,
32        Done,
33    }
34    impl MultipartBody {
35        pub(crate) fn get_body_before_media(&self) -> String {
36            let json_str = self.json_body.to_string();
37            let media = &self.media_body;
38
39            let body_string = format!(
40                "--{}\r\n\
41        Content-Type: application/json; charset=UTF-8\r\n\
42        Content-Length: {}\r\n\
43        \r\n\
44        {}\r\n\
45        \r\n\
46        --{}\r\n\
47        Content-Type: {}\r\n\
48        Content-Length: {}\r\n\
49        \r\n",
50                self.boundary,
51                json_str.len(),
52                json_str,
53                self.boundary,
54                media.mime_type,
55                media.length
56            );
57            body_string
58        }
59        pub(crate) fn get_body_after_media(&self) -> String {
60            format!("\r\n--{}--\r\n", self.boundary,)
61        }
62    }
63}
64mod upload {
65    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, Default)]
66    pub enum MediaUploadProtocol {
67        #[default]
68        None,
69        Simple,
70        Resumable,
71    }
72
73    use crate::media::resumable::LimitedStream;
74    use bytes::{Buf, Bytes};
75    use core::fmt::Debug;
76    use futures_core::Stream;
77    use futures_util::lock::Mutex;
78    use futures_util::{AsyncRead, AsyncSeek};
79    use serde::{Deserialize, Serialize};
80    use std::error::Error;
81    use std::pin::Pin;
82    #[derive(derive_more::Debug)]
83    pub struct MediaUpload<T: MediaUploadStream + ?Sized> {
84        pub(crate) mime_type: String,
85        #[debug(skip)]
86        pub(crate) body: Pin<Box<T>>,
87        pub(crate) length: u64,
88    }
89    impl<T> MediaUpload<T>
90    where
91        T: MediaUploadStream + ?Sized,
92    {
93        pub fn new(mime_type: impl Into<String>, body: Pin<Box<T>>, length: u64) -> Self {
94            Self {
95                mime_type: mime_type.into(),
96                body,
97                length,
98            }
99        }
100    }
101    #[derive(derive_more::Debug)]
102    pub struct AsyncMediaUpload<T: AsyncMediaUploadStream + ?Sized> {
103        pub(crate) mime_type: String,
104        #[debug(skip)]
105        pub(crate) body: LimitedStream<T>,
106        pub(crate) length: u64,
107    }
108    impl<T> AsyncMediaUpload<T>
109    where
110        T: AsyncMediaUploadStream + ?Sized,
111    {
112        pub fn new(mime_type: impl Into<String>, body: LimitedStream<T>, length: u64) -> Self {
113            Self {
114                mime_type: mime_type.into(),
115                body,
116                length,
117            }
118        }
119    }
120    pub trait MediaUploadStream:
121        Stream<Item = Result<Bytes, Box<dyn Error + Send + Sync>>> + Send + Sync + 'static
122    {
123    }
124    impl<T> MediaUploadStream for T where
125        T: Stream<Item = Result<Bytes, Box<dyn Error + Send + Sync>>> + Send + Sync + 'static
126    {
127    }
128
129    pub trait AsyncMediaUploadStream:
130        AsyncRead + AsyncSeek + Debug + Send + Sync + 'static
131    {
132    }
133    impl<T> AsyncMediaUploadStream for T where T: AsyncRead + Debug + AsyncSeek + Send + Sync + 'static {}
134}
135mod download {
136    use core::fmt::Debug;
137    use http_body_util::BodyDataStream;
138    use hyper::body::Incoming;
139    use hyper::HeaderMap;
140    use std::io::Write;
141
142    #[derive(Debug)]
143    pub struct MediaDownload<T: MediaDownloadStream + ?Sized> {
144        pub(crate) stream: Box<T>,
145    }
146
147    impl<T: MediaDownloadStream + ?Sized> MediaDownload<T> {
148        pub(crate) fn get_range(&self) -> Option<String> {
149            // todo!("somehow get some sort of range string")
150            None
151        }
152    }
153
154    impl<T: MediaDownloadStream + ?Sized> MediaDownload<T> {
155        pub fn new(stream: Box<T>) -> Self {
156            Self { stream }
157        }
158    }
159
160    pub trait MediaDownloadStream: Write + Debug + Send + Sync {}
161    impl<T> MediaDownloadStream for T where T: Write + Debug + Send + Sync {}
162
163    pub struct MediaDownloadResponseStream {
164        pub stream: BodyDataStream<Incoming>,
165        pub headers: HeaderMap,
166    }
167}