Skip to main content

internetarchive_rs/
client_uploader_traits_impl.rs

1use std::future::Future;
2use std::path::Path;
3
4use client_uploader_traits::{
5    ClientContext, CreatePublication, CreatePublicationRequest, DownloadNamedPublicFile,
6    ExistingFileConflictPolicy, ExistingFileConflictPolicyKind, ListResourceFiles,
7    MaybeAuthenticatedClient, PublicationOutcome, ReadPublicResource, RepositoryFile,
8    RepositoryRecord, SearchPublicResources, SearchResultsLike, UpdatePublication,
9    UpdatePublicationRequest, UploadSourceKind, UploadSpecLike,
10};
11
12use crate::downloads::ResolvedDownload;
13use crate::endpoint::Endpoint;
14use crate::error::InternetArchiveError;
15use crate::metadata::ItemMetadata;
16use crate::model::{Item, ItemFile, SearchDocument, SearchResponse, SearchResultPage};
17use crate::poll::PollOptions;
18use crate::search::SearchQuery;
19use crate::upload::{FileConflictPolicy, UploadSource, UploadSpec};
20use crate::workflow::{PublishOutcome, PublishRequest};
21use crate::{InternetArchiveClient, ItemIdentifier};
22
23fn into_create_publish_request(
24    request: CreatePublicationRequest<ItemIdentifier, ItemMetadata, UploadSpec>,
25) -> PublishRequest {
26    PublishRequest::new(request.target, request.metadata, request.uploads)
27}
28
29fn into_update_publish_request(
30    request: UpdatePublicationRequest<ItemIdentifier, ItemMetadata, FileConflictPolicy, UploadSpec>,
31) -> PublishRequest {
32    let mut publish_request =
33        PublishRequest::new(request.resource_id, request.metadata, request.uploads);
34    publish_request.conflict_policy = request.policy;
35    publish_request
36}
37
38impl ClientContext for InternetArchiveClient {
39    type Endpoint = Endpoint;
40    type PollOptions = PollOptions;
41    type Error = InternetArchiveError;
42
43    fn endpoint(&self) -> &Self::Endpoint {
44        self.endpoint()
45    }
46
47    fn poll_options(&self) -> &Self::PollOptions {
48        self.poll_options()
49    }
50
51    fn request_timeout(&self) -> Option<std::time::Duration> {
52        self.request_timeout()
53    }
54
55    fn connect_timeout(&self) -> Option<std::time::Duration> {
56        self.connect_timeout()
57    }
58}
59
60impl MaybeAuthenticatedClient for InternetArchiveClient {
61    fn has_auth(&self) -> bool {
62        self.has_auth()
63    }
64}
65
66impl UploadSpecLike for UploadSpec {
67    fn filename(&self) -> &str {
68        &self.filename
69    }
70
71    fn source_kind(&self) -> UploadSourceKind {
72        match &self.source {
73            UploadSource::Path(_) => UploadSourceKind::Path,
74            UploadSource::Bytes(_) => UploadSourceKind::Bytes,
75        }
76    }
77
78    fn content_length(&self) -> Option<u64> {
79        match &self.source {
80            UploadSource::Path(path) => std::fs::metadata(path).ok().map(|metadata| metadata.len()),
81            UploadSource::Bytes(bytes) => u64::try_from(bytes.len()).ok(),
82        }
83    }
84
85    fn content_type(&self) -> Option<&str> {
86        Some(self.content_type.as_ref())
87    }
88}
89
90impl RepositoryFile for ItemFile {
91    type Id = String;
92
93    fn file_id(&self) -> Option<Self::Id> {
94        None
95    }
96
97    fn file_name(&self) -> &str {
98        &self.name
99    }
100
101    fn size_bytes(&self) -> Option<u64> {
102        self.size
103    }
104
105    fn checksum(&self) -> Option<&str> {
106        self.md5
107            .as_deref()
108            .or(self.sha1.as_deref())
109            .or(self.crc32.as_deref())
110    }
111}
112
113impl RepositoryRecord for Item {
114    type Id = ItemIdentifier;
115    type File = ItemFile;
116
117    fn resource_id(&self) -> Option<Self::Id> {
118        self.identifier()
119    }
120
121    fn title(&self) -> Option<&str> {
122        self.metadata.title()
123    }
124
125    fn files(&self) -> &[Self::File] {
126        &self.files
127    }
128}
129
130impl SearchResultsLike for SearchResultPage {
131    type Item = SearchDocument;
132
133    fn items(&self) -> &[Self::Item] {
134        &self.docs
135    }
136
137    fn total_hits(&self) -> Option<u64> {
138        Some(self.num_found)
139    }
140}
141
142impl SearchResultsLike for SearchResponse {
143    type Item = SearchDocument;
144
145    fn items(&self) -> &[Self::Item] {
146        &self.response.docs
147    }
148
149    fn total_hits(&self) -> Option<u64> {
150        Some(self.response.num_found)
151    }
152}
153
154impl PublicationOutcome for PublishOutcome {
155    type PublicResource = Item;
156
157    fn public_resource(&self) -> &Self::PublicResource {
158        &self.item
159    }
160
161    fn created(&self) -> Option<bool> {
162        Some(self.created)
163    }
164}
165
166impl ExistingFileConflictPolicy for FileConflictPolicy {
167    fn kind(&self) -> ExistingFileConflictPolicyKind {
168        match self {
169            Self::Error => ExistingFileConflictPolicyKind::Error,
170            Self::Skip => ExistingFileConflictPolicyKind::Skip,
171            Self::Overwrite => ExistingFileConflictPolicyKind::Overwrite,
172            Self::OverwriteKeepingHistory => {
173                ExistingFileConflictPolicyKind::OverwriteKeepingHistory
174            }
175        }
176    }
177}
178
179impl ReadPublicResource for InternetArchiveClient {
180    type ResourceId = ItemIdentifier;
181    type Resource = Item;
182
183    fn get_public_resource(
184        &self,
185        id: &Self::ResourceId,
186    ) -> impl Future<Output = Result<Self::Resource, Self::Error>> {
187        self.get_item(id)
188    }
189}
190
191impl SearchPublicResources for InternetArchiveClient {
192    type Query = SearchQuery;
193    type SearchResults = SearchResponse;
194
195    fn search_public_resources(
196        &self,
197        query: &Self::Query,
198    ) -> impl Future<Output = Result<Self::SearchResults, Self::Error>> {
199        self.search(query)
200    }
201}
202
203impl ListResourceFiles for InternetArchiveClient {
204    type ResourceId = ItemIdentifier;
205    type File = ItemFile;
206
207    async fn list_resource_files(
208        &self,
209        id: &Self::ResourceId,
210    ) -> Result<Vec<Self::File>, Self::Error> {
211        Ok(self.get_item(id).await?.files)
212    }
213}
214
215impl DownloadNamedPublicFile for InternetArchiveClient {
216    type ResourceId = ItemIdentifier;
217    type Download = ResolvedDownload;
218
219    async fn download_named_public_file_to_path(
220        &self,
221        id: &Self::ResourceId,
222        name: &str,
223        path: &Path,
224    ) -> Result<Self::Download, Self::Error> {
225        self.download_to_path(id, name, path).await?;
226        self.resolve_download(id, name)
227    }
228}
229
230impl CreatePublication for InternetArchiveClient {
231    type CreateTarget = ItemIdentifier;
232    type Metadata = ItemMetadata;
233    type Upload = UploadSpec;
234    type Output = PublishOutcome;
235
236    fn create_publication(
237        &self,
238        request: CreatePublicationRequest<Self::CreateTarget, Self::Metadata, Self::Upload>,
239    ) -> impl Future<Output = Result<Self::Output, Self::Error>> {
240        self.publish_item(into_create_publish_request(request))
241    }
242}
243
244impl UpdatePublication for InternetArchiveClient {
245    type ResourceId = ItemIdentifier;
246    type Metadata = ItemMetadata;
247    type FilePolicy = FileConflictPolicy;
248    type Upload = UploadSpec;
249    type Output = PublishOutcome;
250
251    fn update_publication(
252        &self,
253        request: UpdatePublicationRequest<
254            Self::ResourceId,
255            Self::Metadata,
256            Self::FilePolicy,
257            Self::Upload,
258        >,
259    ) -> impl Future<Output = Result<Self::Output, Self::Error>> {
260        self.upsert_item(into_update_publish_request(request))
261    }
262}