use std::{
fs::{self, File},
path::Path,
};
use tokio::{fs as tokio_fs, io::AsyncWrite};
use crate::{
channel::{Channel, LazyWriter},
client::Client,
error::Result,
frame,
pstream::PObject,
};
pub async fn download(ch: &mut Channel, client: &Client, file_id: &str, dest: &Path) -> Result<()> {
let dest = dest.to_path_buf();
let mut writer = LazyWriter::new(move || {
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}
let file = File::create(&dest)?;
Ok(tokio_fs::File::from_std(file))
});
download_to(ch, client, file_id, &mut writer).await?;
Ok(())
}
pub async fn download_to<W: AsyncWrite + Unpin + Send>(
ch: &mut Channel,
client: &Client,
file_id: &str,
dest: &mut W,
) -> Result<PObject> {
let request = build_download_request(client, file_id)?;
tracing::debug!(file_id, "sending download request");
ch.send(frame::SCMD_REQUEST, &request).await?;
tracing::debug!(file_id, "receiving download response");
let result = ch.recv_download(dest).await;
tracing::debug!(file_id, "download complete");
result
}
const MIN_FILE_ID_BUILD: u64 = 12001;
fn build_download_request(client: &Client, file_id: &str) -> Result<PObject> {
if client.server_build < MIN_FILE_ID_BUILD {
return Err(crate::Error::UnsupportedServer {
build: client.server_build,
min_build: MIN_FILE_ID_BUILD,
reason: "downloads by file ID require id:-style paths, \
which are not supported on this server build"
.into(),
});
}
Ok(client.build_request(
"download",
pmap! {
"sync_id" => 0u64,
"file" => pmap! { "offset" => 0u64 },
"path" => PObject::Str(format!("id:{file_id}")),
},
))
}