1use super::client::DiscourseClient;
2use super::error::http_error;
3use anyhow::{Context, Result, anyhow};
4use serde::{Deserialize, Serialize};
5use std::path::Path;
6
7#[derive(Debug, Deserialize, Serialize, Clone)]
11pub struct UploadInfo {
12 pub id: u64,
13 pub url: String,
14 #[serde(default)]
15 pub short_url: Option<String>,
16 #[serde(default)]
17 pub short_path: Option<String>,
18 pub original_filename: String,
19 pub filesize: u64,
20 #[serde(default)]
21 pub width: Option<u64>,
22 #[serde(default)]
23 pub height: Option<u64>,
24}
25
26impl DiscourseClient {
27 pub fn upload_file(&self, file_path: &Path, upload_type: &str) -> Result<UploadInfo> {
31 let make_form = || -> Result<reqwest::blocking::multipart::Form> {
32 let bytes = std::fs::read(file_path)
33 .with_context(|| format!("reading {}", file_path.display()))?;
34 let filename = file_path
35 .file_name()
36 .and_then(|s| s.to_str())
37 .ok_or_else(|| anyhow!("upload path missing filename: {}", file_path.display()))?
38 .to_string();
39 let part = reqwest::blocking::multipart::Part::bytes(bytes).file_name(filename);
40 Ok(reqwest::blocking::multipart::Form::new()
41 .part("file", part)
42 .text("type", upload_type.to_string())
43 .text("synchronous", "true".to_string()))
44 };
45
46 let response = self.send_retrying(|| Ok(self.post("/uploads.json")?.multipart(make_form()?)))?;
47 let status = response.status();
48 let text = response.text().context("reading upload response body")?;
49 if !status.is_success() {
50 return Err(http_error("upload request", status, &text));
51 }
52 let info: UploadInfo =
53 serde_json::from_str(&text).context("parsing upload response")?;
54 Ok(info)
55 }
56}