optative_process_pool/
resource.rs1use std::io::Write;
2use tempfile::NamedTempFile;
3
4#[derive(Clone, Debug, PartialEq)]
5#[non_exhaustive]
6pub enum Resource {
7 String(String),
8 File { content: String },
9}
10
11impl From<&str> for Resource {
12 fn from(s: &str) -> Self {
13 Resource::String(s.to_string())
14 }
15}
16
17impl From<String> for Resource {
18 fn from(s: String) -> Self {
19 Resource::String(s)
20 }
21}
22
23pub(crate) struct ResolvedResource {
24 pub value: String,
25 pub handle: Option<NamedTempFile>,
26}
27
28impl Resource {
29 pub(crate) fn resolve(&self) -> Result<ResolvedResource, std::io::Error> {
30 match self {
31 Resource::String(s) => Ok(ResolvedResource {
32 value: s.clone(),
33 handle: None,
34 }),
35 Resource::File { content } => {
36 let mut file = NamedTempFile::new()?;
37 file.write_all(content.as_bytes())?;
38 file.flush()?;
39 let path = file.path().to_string_lossy().into_owned();
40 Ok(ResolvedResource {
41 value: path,
42 handle: Some(file),
43 })
44 }
45 }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn from_str_creates_string_variant() {
55 let r: Resource = "--verbose".into();
56 assert_eq!(r, Resource::String("--verbose".into()));
57 }
58
59 #[test]
60 fn from_owned_string_creates_string_variant() {
61 let r: Resource = String::from("hello").into();
62 assert_eq!(r, Resource::String("hello".into()));
63 }
64
65 #[test]
66 fn string_resolves_to_itself() {
67 let r = Resource::String("value".into());
68 let resolved = r.resolve().unwrap();
69 assert_eq!(resolved.value, "value");
70 assert!(resolved.handle.is_none());
71 }
72
73 #[test]
74 fn file_resolves_to_a_readable_path() {
75 let r = Resource::File {
76 content: "hello from file".into(),
77 };
78 let resolved = r.resolve().unwrap();
79 assert!(
80 std::path::Path::new(&resolved.value).exists(),
81 "resolved path should exist on disk"
82 );
83 assert_eq!(
84 std::fs::read_to_string(&resolved.value).unwrap(),
85 "hello from file"
86 );
87 assert!(resolved.handle.is_some());
88 }
89
90 #[test]
91 fn file_is_cleaned_up_when_handle_drops() {
92 let r = Resource::File {
93 content: "ephemeral".into(),
94 };
95 let resolved = r.resolve().unwrap();
96 let path = resolved.value.clone();
97 assert!(std::path::Path::new(&path).exists());
98 drop(resolved.handle);
99 assert!(
100 !std::path::Path::new(&path).exists(),
101 "file should be deleted after handle is dropped"
102 );
103 }
104}