Skip to main content

awsim_core/
body.rs

1use std::io;
2use std::path::PathBuf;
3
4#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
5pub enum Body {
6    InMemory(Vec<u8>),
7    OnDisk(PathBuf),
8}
9
10impl Body {
11    pub fn read_all(&self) -> io::Result<Vec<u8>> {
12        match self {
13            Self::InMemory(b) => Ok(b.clone()),
14            Self::OnDisk(p) => std::fs::read(p),
15        }
16    }
17
18    pub fn read_string(&self) -> io::Result<String> {
19        let bytes = self.read_all()?;
20        String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
21    }
22
23    pub fn len_hint(&self) -> Option<u64> {
24        match self {
25            Self::InMemory(b) => Some(b.len() as u64),
26            Self::OnDisk(p) => std::fs::metadata(p).ok().map(|m| m.len()),
27        }
28    }
29
30    pub fn from_bytes(bytes: Vec<u8>) -> Self {
31        Self::InMemory(bytes)
32    }
33
34    pub fn from_string(s: String) -> Self {
35        Self::InMemory(s.into_bytes())
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use std::io::Write;
43
44    #[test]
45    fn in_memory_read_all_returns_bytes() {
46        let body = Body::InMemory(b"hi".to_vec());
47        assert_eq!(body.read_all().unwrap(), b"hi");
48    }
49
50    #[test]
51    fn in_memory_read_string_returns_utf8() {
52        let body = Body::InMemory(b"hi".to_vec());
53        assert_eq!(body.read_string().unwrap(), "hi");
54    }
55
56    #[test]
57    fn in_memory_read_string_invalid_utf8_errors() {
58        let body = Body::InMemory(vec![0xFF, 0xFE]);
59        let err = body.read_string().unwrap_err();
60        assert_eq!(err.kind(), io::ErrorKind::InvalidData);
61    }
62
63    #[test]
64    fn on_disk_round_trip() {
65        let dir = std::env::temp_dir().join(format!("awsim-body-test-{}", std::process::id()));
66        std::fs::create_dir_all(&dir).unwrap();
67        let path = dir.join("blob.bin");
68        let mut f = std::fs::File::create(&path).unwrap();
69        f.write_all(b"hello world").unwrap();
70        drop(f);
71
72        let body = Body::OnDisk(path.clone());
73        assert_eq!(body.read_all().unwrap(), b"hello world");
74        assert_eq!(body.read_string().unwrap(), "hello world");
75        assert_eq!(body.len_hint(), Some(11));
76
77        let _ = std::fs::remove_file(&path);
78        let _ = std::fs::remove_dir(&dir);
79    }
80
81    #[test]
82    fn len_hint_in_memory() {
83        let body = Body::InMemory(vec![0; 42]);
84        assert_eq!(body.len_hint(), Some(42));
85    }
86
87    #[test]
88    fn len_hint_on_disk_missing_returns_none() {
89        let body = Body::OnDisk(PathBuf::from("/nonexistent/awsim/path/does/not/exist"));
90        assert_eq!(body.len_hint(), None);
91    }
92
93    #[test]
94    fn from_bytes_constructs_in_memory() {
95        let body = Body::from_bytes(b"abc".to_vec());
96        match body {
97            Body::InMemory(b) => assert_eq!(b, b"abc"),
98            _ => panic!("expected InMemory"),
99        }
100    }
101
102    #[test]
103    fn from_string_constructs_in_memory() {
104        let body = Body::from_string("abc".to_string());
105        assert_eq!(body.read_string().unwrap(), "abc");
106    }
107}