mongodb/action/gridfs/
download.rs1use crate::bson::{doc, Bson};
2
3use crate::{
4 action::{action_impl, deeplink, export_doc, option_setters, options_doc},
5 error::{ErrorKind, GridFsErrorKind, GridFsFileIdentifier, Result},
6 gridfs::{
7 FilesCollectionDocument,
8 GridFsBucket,
9 GridFsDownloadByNameOptions,
10 GridFsDownloadStream,
11 },
12};
13
14impl GridFsBucket {
15 #[deeplink]
20 pub fn open_download_stream(&self, id: Bson) -> OpenDownloadStream<'_> {
21 OpenDownloadStream { bucket: self, id }
22 }
23
24 #[deeplink]
34 #[options_doc(download_by_name)]
35 pub fn open_download_stream_by_name(
36 &self,
37 filename: impl Into<String>,
38 ) -> OpenDownloadStreamByName<'_> {
39 OpenDownloadStreamByName {
40 bucket: self,
41 filename: filename.into(),
42 options: None,
43 }
44 }
45
46 async fn find_file_by_id(&self, id: &Bson) -> Result<FilesCollectionDocument> {
49 match self.find_one(doc! { "_id": { "$eq": id } }).await? {
50 Some(file) => Ok(file),
51 None => Err(ErrorKind::GridFs(GridFsErrorKind::FileNotFound {
52 identifier: GridFsFileIdentifier::Id(id.clone()),
53 })
54 .into()),
55 }
56 }
57
58 async fn find_file_by_name(
59 &self,
60 filename: &str,
61 options: Option<GridFsDownloadByNameOptions>,
62 ) -> Result<FilesCollectionDocument> {
63 let revision = options.and_then(|opts| opts.revision).unwrap_or(-1);
64 let (sort, skip) = if revision >= 0 {
65 (1, revision)
66 } else {
67 (-1, -revision - 1)
68 };
69 let skip: u64 = skip.try_into().unwrap();
71
72 match self
73 .files()
74 .find_one(doc! { "filename": filename })
75 .sort(doc! { "uploadDate": sort })
76 .skip(skip)
77 .await?
78 {
79 Some(fcd) => Ok(fcd),
80 None => {
81 if self
82 .files()
83 .find_one(doc! { "filename": filename })
84 .await?
85 .is_some()
86 {
87 Err(ErrorKind::GridFs(GridFsErrorKind::RevisionNotFound { revision }).into())
88 } else {
89 Err(ErrorKind::GridFs(GridFsErrorKind::FileNotFound {
90 identifier: GridFsFileIdentifier::Filename(filename.into()),
91 })
92 .into())
93 }
94 }
95 }
96 }
97}
98
99#[cfg(feature = "sync")]
100impl crate::sync::gridfs::GridFsBucket {
101 #[deeplink]
106 pub fn open_download_stream(&self, id: Bson) -> OpenDownloadStream<'_> {
107 self.async_bucket.open_download_stream(id)
108 }
109
110 #[deeplink]
120 #[options_doc(download_by_name, "run")]
121 pub fn open_download_stream_by_name(
122 &self,
123 filename: impl Into<String>,
124 ) -> OpenDownloadStreamByName<'_> {
125 self.async_bucket.open_download_stream_by_name(filename)
126 }
127}
128
129#[must_use]
133pub struct OpenDownloadStream<'a> {
134 bucket: &'a GridFsBucket,
135 id: Bson,
136}
137
138#[action_impl(sync = crate::sync::gridfs::GridFsDownloadStream)]
139impl<'a> Action for OpenDownloadStream<'a> {
140 type Future = OpenDownloadStreamFuture;
141
142 async fn execute(self) -> Result<GridFsDownloadStream> {
143 let file = self.bucket.find_file_by_id(&self.id).await?;
144 GridFsDownloadStream::new(file, self.bucket.chunks()).await
145 }
146}
147
148#[must_use]
152pub struct OpenDownloadStreamByName<'a> {
153 bucket: &'a GridFsBucket,
154 filename: String,
155 options: Option<GridFsDownloadByNameOptions>,
156}
157
158#[option_setters(crate::gridfs::GridFsDownloadByNameOptions)]
159#[export_doc(download_by_name)]
160impl OpenDownloadStreamByName<'_> {}
161
162#[action_impl(sync = crate::sync::gridfs::GridFsDownloadStream)]
163impl<'a> Action for OpenDownloadStreamByName<'a> {
164 type Future = OpenDownloadStreamByNameFuture;
165
166 async fn execute(self) -> Result<GridFsDownloadStream> {
167 let file = self
168 .bucket
169 .find_file_by_name(&self.filename, self.options)
170 .await?;
171 GridFsDownloadStream::new(file, self.bucket.chunks()).await
172 }
173}