Skip to main content

ferrin_spec/
files.rs

1//! Provider file storage interface.
2
3use std::future::Future;
4
5use bytes::Bytes;
6use chrono::DateTime;
7use chrono::Utc;
8use serde::Deserialize;
9use serde::Serialize;
10use tokio_util::sync::CancellationToken;
11
12use crate::dynamic::BoxStream;
13use crate::error::ProviderError;
14use crate::shared::Headers;
15use crate::shared::MediaType;
16use crate::shared::ProviderId;
17use crate::shared::ProviderMetadata;
18use crate::shared::ProviderOptions;
19use crate::shared::ProviderReference;
20use crate::shared::Warning;
21
22/// Upload, inspect, download and delete files stored at the provider.
23///
24/// Only [`upload_file`](Self::upload_file) is required; the other operations
25/// are gated by `supports_*` queries and default to an
26/// `UnsupportedFunctionality` error.
27pub trait Files: Send + Sync + 'static {
28    /// Provider identifier.
29    fn provider(&self) -> &ProviderId;
30
31    /// Uploads a file and returns its provider reference.
32    fn upload_file(
33        &self,
34        options: UploadFileOptions,
35    ) -> impl Future<Output = Result<UploadFileResult, ProviderError>> + Send;
36
37    /// Whether [`get_file_metadata`](Self::get_file_metadata) is implemented.
38    fn supports_get_file_metadata(&self) -> bool {
39        false
40    }
41
42    /// Fetches metadata of an uploaded file.
43    fn get_file_metadata(
44        &self,
45        options: FileReferenceOptions,
46    ) -> impl Future<Output = Result<FileMetadataResult, ProviderError>> + Send {
47        let _ = options;
48        std::future::ready(Err(ProviderError::unsupported("get_file_metadata")))
49    }
50
51    /// Whether [`download_file`](Self::download_file) is implemented.
52    fn supports_download_file(&self) -> bool {
53        false
54    }
55
56    /// Downloads an uploaded file.
57    fn download_file(
58        &self,
59        options: FileReferenceOptions,
60    ) -> impl Future<Output = Result<DownloadFileResult, ProviderError>> + Send {
61        let _ = options;
62        std::future::ready(Err(ProviderError::unsupported("download_file")))
63    }
64
65    /// Whether [`delete_file`](Self::delete_file) is implemented.
66    fn supports_delete_file(&self) -> bool {
67        false
68    }
69
70    /// Deletes an uploaded file.
71    fn delete_file(
72        &self,
73        options: FileReferenceOptions,
74    ) -> impl Future<Output = Result<DeleteFileResult, ProviderError>> + Send {
75        let _ = options;
76        std::future::ready(Err(ProviderError::unsupported("delete_file")))
77    }
78}
79
80/// Data to upload.
81pub enum UploadData {
82    /// In-memory bytes.
83    Bytes(Bytes),
84    /// UTF-8 text.
85    Text(String),
86    /// A stream of chunks.
87    Stream(BoxStream<'static, Result<Bytes, ProviderError>>),
88}
89
90impl std::fmt::Debug for UploadData {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            Self::Bytes(bytes) => f.debug_tuple("Bytes").field(&bytes.len()).finish(),
94            Self::Text(text) => f.debug_tuple("Text").field(&text.len()).finish(),
95            Self::Stream(_) => f.write_str("Stream(<stream>)"),
96        }
97    }
98}
99
100impl From<Bytes> for UploadData {
101    fn from(bytes: Bytes) -> Self {
102        Self::Bytes(bytes)
103    }
104}
105
106impl From<Vec<u8>> for UploadData {
107    fn from(bytes: Vec<u8>) -> Self {
108        Self::Bytes(Bytes::from(bytes))
109    }
110}
111
112impl From<String> for UploadData {
113    fn from(text: String) -> Self {
114        Self::Text(text)
115    }
116}
117
118/// Options for uploading a file.
119#[derive(Debug)]
120pub struct UploadFileOptions {
121    /// The data.
122    pub data: UploadData,
123    /// Media type of the data.
124    pub media_type: MediaType,
125    /// File name.
126    pub filename: Option<String>,
127    /// Additional request headers.
128    pub headers: Headers,
129    /// Provider-specific options keyed by provider name.
130    pub provider_options: ProviderOptions,
131    /// Cancellation token.
132    pub cancellation: CancellationToken,
133}
134
135impl UploadFileOptions {
136    /// Creates options for `data` of `media_type`.
137    #[must_use]
138    pub fn new(data: impl Into<UploadData>, media_type: impl Into<MediaType>) -> Self {
139        Self {
140            data: data.into(),
141            media_type: media_type.into(),
142            filename: None,
143            headers: Headers::new(),
144            provider_options: ProviderOptions::new(),
145            cancellation: CancellationToken::new(),
146        }
147    }
148}
149
150/// Options for operations on an existing file.
151#[derive(Debug, Clone)]
152pub struct FileReferenceOptions {
153    /// Reference of the file.
154    pub file: ProviderReference,
155    /// Additional request headers.
156    pub headers: Headers,
157    /// Provider-specific options keyed by provider name.
158    pub provider_options: ProviderOptions,
159    /// Cancellation token.
160    pub cancellation: CancellationToken,
161}
162
163impl FileReferenceOptions {
164    /// Creates options for `file`.
165    #[must_use]
166    pub fn new(file: ProviderReference) -> Self {
167        Self {
168            file,
169            headers: Headers::new(),
170            provider_options: ProviderOptions::new(),
171            cancellation: CancellationToken::new(),
172        }
173    }
174}
175
176/// Result of an upload.
177#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
178pub struct UploadFileResult {
179    /// Reference to the stored file.
180    pub provider_reference: ProviderReference,
181    /// Media type as stored.
182    #[serde(default, skip_serializing_if = "Option::is_none")]
183    pub media_type: Option<MediaType>,
184    /// File name as stored.
185    #[serde(default, skip_serializing_if = "Option::is_none")]
186    pub filename: Option<String>,
187    /// Size in bytes.
188    #[serde(default, skip_serializing_if = "Option::is_none")]
189    pub byte_size: Option<u64>,
190    /// Creation time.
191    #[serde(default, skip_serializing_if = "Option::is_none")]
192    pub created_at: Option<DateTime<Utc>>,
193    /// Expiry time.
194    #[serde(default, skip_serializing_if = "Option::is_none")]
195    pub expires_at: Option<DateTime<Utc>>,
196    /// Provider-specific metadata.
197    #[serde(default, skip_serializing_if = "Option::is_none")]
198    pub provider_metadata: Option<ProviderMetadata>,
199    /// Warnings.
200    #[serde(default)]
201    pub warnings: Vec<Warning>,
202}
203
204/// Metadata of a stored file.
205pub type FileMetadataResult = UploadFileResult;
206
207/// Result of a download.
208pub struct DownloadFileResult {
209    /// File content.
210    pub content: BoxStream<'static, Result<Bytes, ProviderError>>,
211    /// Media type, if known.
212    pub media_type: Option<MediaType>,
213    /// Provider-specific metadata.
214    pub provider_metadata: Option<ProviderMetadata>,
215    /// Warnings.
216    pub warnings: Vec<Warning>,
217}
218
219impl std::fmt::Debug for DownloadFileResult {
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221        f.debug_struct("DownloadFileResult")
222            .field("content", &"<stream>")
223            .field("media_type", &self.media_type)
224            .field("provider_metadata", &self.provider_metadata)
225            .field("warnings", &self.warnings)
226            .finish()
227    }
228}
229
230/// Result of a delete.
231#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
232pub struct DeleteFileResult {
233    /// Reference of the deleted file.
234    pub provider_reference: ProviderReference,
235    /// Whether the file was deleted.
236    pub deleted: bool,
237    /// Provider-specific metadata.
238    #[serde(default, skip_serializing_if = "Option::is_none")]
239    pub provider_metadata: Option<ProviderMetadata>,
240    /// Warnings.
241    #[serde(default)]
242    pub warnings: Vec<Warning>,
243}