Private
Public Access
1
0

Started working on attachment store

This commit is contained in:
2025-05-15 20:11:10 -07:00
parent 77177e07aa
commit 0d4c2e5104
10 changed files with 721 additions and 307 deletions

View File

@@ -0,0 +1,104 @@
use std::{
io::{BufReader, BufWriter, Read, Write},
path::{Path, PathBuf},
};
use anyhow::{Error, Result};
use futures_util::{poll, StreamExt};
use kordophone::APIInterface;
use thiserror::Error;
use tokio::pin;
mod target {
pub static ATTACHMENTS: &str = "attachments";
}
#[derive(Debug, Clone)]
pub struct Attachment {
pub guid: String,
pub path: PathBuf,
pub downloaded: bool,
}
#[derive(Debug, Error)]
enum AttachmentStoreError {
#[error("attachment has already been downloaded")]
AttachmentAlreadyDownloaded,
#[error("Client error: {0}")]
APIClientError(String),
}
pub struct AttachmentStore {
store_path: PathBuf,
}
impl AttachmentStore {
pub fn new(data_dir: &PathBuf) -> AttachmentStore {
let store_path = data_dir.join("attachments");
log::info!(target: target::ATTACHMENTS, "Attachment store path: {}", store_path.display());
// Create the attachment store if it doesn't exist
std::fs::create_dir_all(&store_path)
.expect("Wasn't able to create the attachment store path");
AttachmentStore {
store_path: store_path,
}
}
pub fn get_attachment(&self, guid: &String) -> Attachment {
let path = self.store_path.join(guid);
let path_exists = std::fs::exists(&path).expect(
format!(
"Wasn't able to check for the existence of an attachment file path at {}",
&path.display()
)
.as_str(),
);
Attachment {
guid: guid.to_owned(),
path: path,
downloaded: path_exists,
}
}
pub async fn download_attachent<C, F>(
&mut self,
attachment: &Attachment,
mut client_factory: F,
) -> Result<()>
where
C: APIInterface,
F: AsyncFnMut() -> Result<C>,
{
if attachment.downloaded {
log::error!(target: target::ATTACHMENTS, "Attempted to download existing attachment.");
return Err(AttachmentStoreError::AttachmentAlreadyDownloaded.into());
}
// Create temporary file first, we'll atomically swap later.
assert!(!std::fs::exists(&attachment.path).unwrap());
let file = std::fs::File::create(&attachment.path)?;
let mut writer = BufWriter::new(&file);
log::trace!(target: target::ATTACHMENTS, "Created attachment file at {}", &attachment.path.display());
let mut client = (client_factory)().await?;
let stream = client
.fetch_attachment_data(&attachment.guid)
.await
.map_err(|e| AttachmentStoreError::APIClientError(format!("{:?}", e)))?;
// Since we're async, we need to pin this.
pin!(stream);
log::trace!(target: target::ATTACHMENTS, "Writing attachment data to disk");
while let Some(Ok(data)) = stream.next().await {
writer.write(data.as_ref())?;
}
Ok(())
}
}