shadow_drive_sdk/
models.rs1use bytes::Bytes;
2use reqwest::multipart::Part;
3use serde::Deserialize;
4use sha2::{Digest, Sha256};
5use std::path::Path;
6use tokio::{fs::File, io::AsyncReadExt};
7
8pub use shadow_drive_user_staking::instructions::{
10 decrease_storage::UnstakeInfo, initialize_account::UserInfo, store_file::File as FileAccount,
11};
12
13pub mod payload;
14pub mod storage_acct;
15
16use crate::{constants::FILE_SIZE_LIMIT, error::Error};
17use payload::Payload;
18
19pub type ShadowDriveResult<T> = Result<T, Error>;
20
21const BUFFER_SIZE: usize = 4096;
22
23#[derive(Clone, Debug, Deserialize)]
24pub struct ShdwDriveResponse {
25 pub txid: String,
26}
27
28#[derive(Clone, Debug, Deserialize)]
29pub struct StorageResponse {
30 pub message: String,
31 pub transaction_signature: String,
32 pub error: Option<String>,
33}
34
35#[derive(Clone, Debug, Deserialize)]
36pub struct CreateStorageAccountResponse {
37 pub shdw_bucket: Option<String>,
38 pub transaction_signature: String,
39}
40
41#[derive(Clone, Debug, Deserialize)]
42pub struct DeleteFileResponse {
43 pub message: String,
44 pub error: Option<String>,
45}
46
47#[derive(Clone, Debug, Deserialize)]
48pub struct GetBucketSizeResponse {
49 pub storage_used: u64,
50 pub error: Option<String>,
51}
52
53#[derive(Debug, Clone)]
55pub struct ShadowFile {
56 pub name: String,
57 pub data: Payload,
58 content_type: String,
59}
60
61const FALLBACK_MIMETYPE: &'static str = "application/octet-stream";
62
63impl ShadowFile {
64 pub fn name(&self) -> &str {
65 &self.name
66 }
67
68 pub fn file<T: AsRef<Path>>(name: String, path: T) -> Self {
69 let content_type = match infer::get_from_path(path.as_ref()) {
70 Ok(mime_option) => mime_option
72 .map(|mime| mime.mime_type())
73 .unwrap_or(FALLBACK_MIMETYPE)
74 .to_owned(),
75
76 Err(_) => FALLBACK_MIMETYPE.to_owned(),
78 };
79 Self {
80 name,
81 content_type,
82 data: Payload::File(path.as_ref().to_owned()),
83 }
84 }
85
86 pub fn bytes<T: Into<Bytes>>(name: String, data: T) -> Self {
87 Self {
88 name,
89 content_type: FALLBACK_MIMETYPE.to_owned(),
90 data: Payload::Bytes(data.into()),
91 }
92 }
93
94 pub(crate) async fn sha256(&self) -> ShadowDriveResult<String> {
95 let result = match &self.data {
96 Payload::File(path) => {
97 let mut file = File::open(path).await.map_err(Error::FileSystemError)?;
98 let mut buf = [0u8; BUFFER_SIZE];
99 let mut hasher = Sha256::new();
100
101 loop {
102 let bytes_read = file.read(&mut buf[..]).await?;
103
104 if bytes_read != 0 {
105 hasher.update(&buf[..bytes_read]);
106 } else {
107 break;
108 }
109 }
110
111 hasher.finalize()
112 }
113 Payload::Bytes(data) => {
114 let mut hasher = Sha256::new();
115 hasher.update(&data);
116 hasher.finalize()
117 }
118 };
119 Ok(hex::encode(result))
120 }
121
122 pub(crate) async fn into_form_part(self) -> ShadowDriveResult<Part> {
123 let mut part = match self.data {
124 Payload::File(path) => {
125 let file = File::open(path).await.map_err(Error::FileSystemError)?;
126 let file_meta = file.metadata().await.map_err(Error::FileSystemError)?;
127
128 if file_meta.len() > FILE_SIZE_LIMIT {
130 return Err(Error::FileTooLarge(self.name.clone()));
131 }
132
133 Part::stream_with_length(file, file_meta.len()).file_name(self.name)
134 }
135 Payload::Bytes(data) => {
136 if data.len() as u64 > FILE_SIZE_LIMIT {
138 return Err(Error::FileTooLarge(self.name.clone()));
139 }
140
141 Part::stream_with_length(Bytes::clone(&data), data.len() as u64)
142 .file_name(self.name)
143 }
144 };
145
146 part = part.mime_str(&self.content_type)?;
147 Ok(part)
148 }
149}
150
151#[derive(Clone, Debug, Deserialize)]
152pub struct ShadowUploadResponse {
153 #[serde(default)]
154 pub finalized_locations: Vec<String>,
155 pub message: String,
156 #[serde(default)]
157 pub upload_errors: Vec<UploadError>,
158}
159
160#[derive(Clone, Debug, Deserialize)]
161pub struct ShadowEditResponse {
162 #[serde(default)]
163 pub finalized_location: String,
164 #[serde(default)]
165 pub error: String,
166}
167
168#[derive(Clone, Debug, Deserialize)]
169pub struct UploadError {
170 pub file: String,
171 pub storage_account: String,
172 pub error: String,
173}
174
175#[allow(dead_code)]
176#[derive(Clone, Debug, Deserialize)]
177pub(crate) struct ShdwDriveBatchServerResponse {
178 pub _finalized_locations: Option<Vec<String>>,
179 pub transaction_signature: String,
180}
181
182#[derive(Clone, Debug, Deserialize)]
183pub enum BatchUploadStatus {
184 Uploaded,
185 AlreadyExists,
186 Error(String),
187}
188#[derive(Clone, Debug, Deserialize)]
189pub struct ShadowBatchUploadResponse {
190 pub file_name: String,
191 pub status: BatchUploadStatus,
192 pub location: Option<String>,
193 pub transaction_signature: Option<String>,
194}
195
196#[derive(Clone, Debug, Deserialize)]
197pub struct FileDataResponse {
198 pub file_data: FileData,
199}
200
201#[derive(Clone, Debug, Deserialize)]
202#[serde(rename_all = "kebab-case")]
203pub struct FileData {
204 pub owner_account_pubkey: String,
205 pub storage_account_pubkey: String,
206}
207
208#[derive(Clone, Debug, Deserialize)]
209pub struct ListObjectsResponse {
210 pub keys: Vec<String>,
211}