1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use super::{Console, UploadConfig, UploadOperator, Uploader};
15 use crate::async_impl::MultiPart;
16 use crate::runtime::AsyncRead;
17 
18 /// A builder that can create a `Uploader`.
19 ///
20 /// You can use this builder to build a `Uploader` step by step.
21 ///
22 /// # Examples
23 ///
24 /// ```
25 /// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader};
26 ///
27 /// let uploader = UploaderBuilder::new()
28 ///     .reader("HelloWorld".as_bytes())
29 ///     .console()
30 ///     .build();
31 /// ```
32 pub struct UploaderBuilder<S> {
33     state: S,
34 }
35 
36 /// A state indicates that `UploaderBuilder` wants a `Reader`.
37 pub struct WantsReader;
38 
39 impl UploaderBuilder<WantsReader> {
40     /// Creates a `UploaderBuilder` in the `WantsReader` state.
41     ///
42     /// # Examples
43     ///
44     /// ```
45     /// # use ylong_http_client::async_impl::UploaderBuilder;
46     ///
47     /// let builder = UploaderBuilder::new();
48     /// ```
new() -> Self49     pub fn new() -> Self {
50         Self { state: WantsReader }
51     }
52 
53     /// Sets a reader that needs to be read.
54     ///
55     /// # Examples
56     ///
57     /// ```
58     /// # use ylong_http_client::async_impl::UploaderBuilder;
59     ///
60     /// let builder = UploaderBuilder::new().reader("HelloWorld".as_bytes());
61     /// ```
reader<R: AsyncRead>(self, reader: R) -> UploaderBuilder<WantsOperator<R>>62     pub fn reader<R: AsyncRead>(self, reader: R) -> UploaderBuilder<WantsOperator<R>> {
63         UploaderBuilder {
64             state: WantsOperator {
65                 reader,
66                 config: UploadConfig::default(),
67             },
68         }
69     }
70 
71     /// Sets a `multipart` that needs to be read. The size of the multipart will
72     /// be set automatically if it contains.
73     ///
74     /// # Examples
75     ///
76     /// ```
77     /// # use ylong_http_client::async_impl::UploaderBuilder;
78     ///
79     /// let builder = UploaderBuilder::new().reader("HelloWorld".as_bytes());
80     /// ```
multipart(self, reader: MultiPart) -> UploaderBuilder<WantsOperator<MultiPart>>81     pub fn multipart(self, reader: MultiPart) -> UploaderBuilder<WantsOperator<MultiPart>> {
82         let total_bytes = reader.total_bytes();
83         UploaderBuilder {
84             state: WantsOperator {
85                 reader,
86                 config: UploadConfig { total_bytes },
87             },
88         }
89     }
90 }
91 
92 impl Default for UploaderBuilder<WantsReader> {
default() -> Self93     fn default() -> Self {
94         Self::new()
95     }
96 }
97 
98 /// A state indicates that `UploaderBuilder` wants an `UploadOperator`.
99 pub struct WantsOperator<R> {
100     reader: R,
101     config: UploadConfig,
102 }
103 
104 impl<R: AsyncRead> UploaderBuilder<WantsOperator<R>> {
105     /// Sets a customized `UploaderOperator`.
106     ///
107     /// Then the `UploaderBuilder` will switch to `WantsConfig` state.
108     ///
109     /// # Examples
110     ///
111     /// ```
112     /// # use std::pin::Pin;
113     /// # use std::task::{Context, Poll};
114     /// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader, UploadOperator, Response};
115     /// # use ylong_http_client::HttpClientError;
116     ///
117     /// struct MyOperator;
118     ///
119     /// impl UploadOperator for MyOperator {
120     ///     fn poll_progress(
121     ///         self: Pin<&mut Self>,
122     ///         cx: &mut Context<'_>,
123     ///         uploaded: u64,
124     ///         total: Option<u64>,
125     ///     ) -> Poll<Result<(), HttpClientError>> {
126     ///         todo!()
127     ///     }
128     /// }
129     ///
130     /// let builder = UploaderBuilder::new()
131     ///     .reader("HelloWorld".as_bytes())
132     ///     .operator(MyOperator);
133     /// ```
operator<T: UploadOperator>(self, operator: T) -> UploaderBuilder<WantsConfig<R, T>>134     pub fn operator<T: UploadOperator>(self, operator: T) -> UploaderBuilder<WantsConfig<R, T>> {
135         UploaderBuilder {
136             state: WantsConfig {
137                 reader: self.state.reader,
138                 operator,
139                 config: self.state.config,
140             },
141         }
142     }
143 
144     /// Sets a `Console` to this `Uploader`. The download result and progress
145     /// will be displayed on the console.
146     ///
147     /// The `Console` needs a `Reader` to display.
148     ///
149     /// # Examples
150     ///
151     /// ```
152     /// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader, Response};
153     ///
154     /// let builder = UploaderBuilder::new()
155     ///     .reader("HelloWorld".as_bytes())
156     ///     .console();
157     /// ```
console(self) -> UploaderBuilder<WantsConfig<R, Console>>158     pub fn console(self) -> UploaderBuilder<WantsConfig<R, Console>> {
159         UploaderBuilder {
160             state: WantsConfig {
161                 reader: self.state.reader,
162                 operator: Console,
163                 config: self.state.config,
164             },
165         }
166     }
167 }
168 
169 /// A state indicates that `UploaderBuilder` wants some configurations.
170 pub struct WantsConfig<R, T> {
171     reader: R,
172     operator: T,
173     config: UploadConfig,
174 }
175 
176 impl<R, T> UploaderBuilder<WantsConfig<R, T>> {
177     /// Sets the total bytes of the uploaded content.
178     ///
179     /// Default is `None` which means that you don't know the size of the
180     /// content.
181     ///
182     /// # Examples
183     ///
184     /// ```
185     /// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader};
186     ///
187     /// let builder = UploaderBuilder::new()
188     ///     .reader("HelloWorld".as_bytes())
189     ///     .console()
190     ///     .total_bytes(Some(10));
191     /// ```
total_bytes(mut self, total_bytes: Option<u64>) -> Self192     pub fn total_bytes(mut self, total_bytes: Option<u64>) -> Self {
193         self.state.config.total_bytes = total_bytes;
194         self
195     }
196 
197     /// Returns a `Uploader` that uses this `UploaderBuilder` configuration.
198     ///
199     /// # Examples
200     ///
201     /// ```
202     /// # use ylong_http_client::async_impl::{UploaderBuilder, Uploader, Response};
203     ///
204     /// let uploader = UploaderBuilder::new()
205     ///     .reader("HelloWorld".as_bytes())
206     ///     .console()
207     ///     .build();
208     /// ```
build(self) -> Uploader<R, T>209     pub fn build(self) -> Uploader<R, T> {
210         Uploader {
211             reader: self.state.reader,
212             operator: self.state.operator,
213             config: self.state.config,
214             info: None,
215         }
216     }
217 }
218