use anyhow::{Context, Result};
use reqwest::Client;
use reqwest::multipart::{Form, Part};
use tracing::debug;
use super::response_text;
pub const API_URL: &str = "https://fastpic.org/upload?api=1";
pub async fn upload(client: &Client, data: Vec<u8>, url: &str) -> Result<String> {
let form = Form::new()
.text("method", "file")
.text("check_thumb", "no")
.text("uploading", "1")
.part("file1", Part::bytes(data).file_name("image"));
let resp = client
.post(url)
.multipart(form)
.send()
.await
.context("failed to send request to fastpic")?;
let body = response_text(resp, "fastpic").await?;
let url = extract_tag(&body, "imagepath");
if url.is_empty() {
debug!("Response text:\n{body}");
anyhow::bail!("image link not found in fastpic response");
}
Ok(url)
}
fn extract_tag(body: &str, tag: &str) -> String {
let open = format!("<{tag}>");
let close = format!("</{tag}>");
let Some(start) = body.find(&open) else {
return String::new();
};
let start = start + open.len();
let Some(end) = body[start..].find(&close) else {
return String::new();
};
body[start..start + end].trim().to_owned()
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use wiremock::matchers::method;
use wiremock::{Mock, MockServer, ResponseTemplate};
use super::*;
#[tokio::test]
async fn test_upload_success() {
let mock_server = MockServer::start().await;
let xml = "<imagepath>https://i122.fastpic.org/big/2023/image.png</imagepath>";
Mock::given(method("POST"))
.respond_with(ResponseTemplate::new(200).set_body_string(xml))
.mount(&mock_server)
.await;
let client = Client::new();
let url = upload(&client, vec![1, 2, 3], &mock_server.uri())
.await
.unwrap();
assert_eq!(url, "https://i122.fastpic.org/big/2023/image.png");
}
#[tokio::test]
async fn test_upload_error_response() {
let mock_server = MockServer::start().await;
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<UploadSettings>
<imagepath></imagepath>
<status>err</status>
<error>Sorry, uploading are not allowed.</error>
</UploadSettings>"#;
Mock::given(method("POST"))
.respond_with(ResponseTemplate::new(200).set_body_string(xml))
.mount(&mock_server)
.await;
let client = Client::new();
let err = upload(&client, vec![1, 2, 3], &mock_server.uri())
.await
.unwrap_err();
assert!(
err.to_string().contains("image link not found"),
"expected image link not found error, got: {err}"
);
}
}