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}