agent_first_http/sdk/fetch/artifacts/
body.rs1use std::path::PathBuf;
4
5use crate::sdk::fetch::writer;
6use crate::shared::artifacts::{Artifact, ArtifactPaths};
7use crate::shared::error::Error;
8
9pub async fn write(
12 paths: &ArtifactPaths,
13 content_type: Option<&str>,
14 bytes: &[u8],
15) -> Result<PathBuf, Error> {
16 let ext = extension_for(content_type);
17 let mut target = paths.file_for(Artifact::Body);
18 target.set_extension(ext);
19 writer::write_bytes(&target, bytes).await?;
20 Ok(target)
21}
22
23fn extension_for(content_type: Option<&str>) -> &'static str {
24 let Some(ct) = content_type else { return "bin" };
25 let primary = ct
26 .split(';')
27 .next()
28 .unwrap_or(ct)
29 .trim()
30 .to_ascii_lowercase();
31 match primary.as_str() {
32 "text/html" | "application/xhtml+xml" => "html",
33 "application/json" | "application/ld+json" => "json",
34 "application/javascript" | "text/javascript" => "js",
35 "text/css" => "css",
36 "text/plain" => "txt",
37 "image/png" => "png",
38 "image/jpeg" | "image/jpg" => "jpg",
39 "image/gif" => "gif",
40 "image/webp" => "webp",
41 "image/svg+xml" => "svg",
42 "application/xml" | "text/xml" => "xml",
43 "application/pdf" => "pdf",
44 "application/octet-stream" => "bin",
45 _ => mime_guess::get_mime_extensions_str(&primary)
46 .and_then(|exts| exts.first())
47 .copied()
48 .unwrap_or("bin"),
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use crate::shared::ids::RequestId;
56
57 #[test]
58 fn extension_table_covers_common_mimes() {
59 assert_eq!(extension_for(Some("text/html")), "html");
60 assert_eq!(extension_for(Some("text/html; charset=utf-8")), "html");
61 assert_eq!(extension_for(Some("application/json")), "json");
62 assert_eq!(extension_for(Some("image/png")), "png");
63 assert_eq!(extension_for(None), "bin");
64 assert_eq!(extension_for(Some("application/x-weird-thing")), "bin");
65 }
66
67 #[tokio::test]
68 async fn writes_body_with_html_extension() {
69 let dir = tempfile::tempdir().unwrap();
70 let rid = RequestId::new_v4();
71 let paths = ArtifactPaths::new(dir.path().to_path_buf(), &rid);
72 let p = write(&paths, Some("text/html"), b"<html></html>")
73 .await
74 .unwrap();
75 assert_eq!(p.extension().and_then(|s| s.to_str()), Some("html"));
76 let content = tokio::fs::read(&p).await.unwrap();
77 assert_eq!(content, b"<html></html>");
78 }
79}