modio/util/upload/mod.rs
1//! Upload interface for mod files.
2
3mod byte_ranges;
4mod error;
5mod multipart;
6
7/// Required size (50MB) of upload parts except the last part.
8pub const MULTIPART_FILE_PART_SIZE: u64 = 50 * 1024 * 1024;
9
10pub use crate::request::files::multipart::ContentRange;
11pub use byte_ranges::{byte_ranges, ByteRanges};
12pub use error::{Error, ErrorKind};
13pub use multipart::MultipartUploader;
14
15use crate::client::Client;
16use crate::types::files::multipart::UploadId;
17use crate::types::id::{GameId, ModId};
18use multipart::{Init, Started};
19
20/// Extension trait for uploading files in multiple parts.
21pub trait MultipartUpload: private::Sealed {
22 /// Returns [`MultipartUploader`] for uploading files in multiple parts.
23 ///
24 /// # Example
25 /// ```no_run
26 /// # use modio::Client;
27 /// #
28 /// # #[tokio::main]
29 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
30 /// # let client = Client::builder("MODIO_API_KEY".to_owned()).build()?;
31 /// use modio::types::id::Id;
32 /// use modio::util::upload::{self, ContentRange, MultipartUpload};
33 /// use tokio::fs::File;
34 /// use tokio::io::{AsyncReadExt, BufReader};
35 /// use tokio_util::io::ReaderStream;
36 ///
37 /// let uploader = client
38 /// .upload(Id::new(51), Id::new(1234), "modfile.zip")
39 /// .nonce("xxxxxx") // Max 64 characters (Recommended: SHA-256)
40 /// .await?;
41 ///
42 /// let file = File::open("modfile.zip").await?;
43 /// let file_size = file.metadata().await?.len();
44 ///
45 /// for (start, end) in upload::byte_ranges(file_size) {
46 /// let input = BufReader::new(file.try_clone().await?);
47 /// let part = input.take(upload::MULTIPART_FILE_PART_SIZE);
48 /// let stream = ReaderStream::new(part);
49 ///
50 /// let range = ContentRange {
51 /// start,
52 /// end,
53 /// total: file_size,
54 /// };
55 ///
56 /// // Add file part to the upload session.
57 /// uploader.add_part(range, stream).await?;
58 /// }
59 ///
60 /// // Complete the multipart upload session.
61 /// let uploader = uploader.complete().await?;
62 ///
63 /// // Finalize upload to the mod with file details.
64 /// uploader.active(true).version("1.0").await?;
65 /// # Ok(())
66 /// # }
67 /// ```
68 fn upload<'a>(
69 &'a self,
70 game_id: GameId,
71 mod_id: ModId,
72 filename: &'a str,
73 ) -> MultipartUploader<'a, Init<'a>>;
74
75 fn upload_for(
76 &self,
77 game_id: GameId,
78 mod_id: ModId,
79 upload_id: UploadId,
80 ) -> MultipartUploader<'_, Started<'_>>;
81}
82
83impl MultipartUpload for Client {
84 fn upload<'a>(
85 &'a self,
86 game_id: GameId,
87 mod_id: ModId,
88 filename: &'a str,
89 ) -> MultipartUploader<'a, Init<'a>> {
90 MultipartUploader::init(self, game_id, mod_id, filename)
91 }
92
93 fn upload_for(
94 &self,
95 game_id: GameId,
96 mod_id: ModId,
97 upload_id: UploadId,
98 ) -> MultipartUploader<'_, Started<'_>> {
99 MultipartUploader::started(self, game_id, mod_id, upload_id)
100 }
101}
102mod private {
103 use crate::Client;
104
105 pub trait Sealed {}
106
107 impl Sealed for Client {}
108}