1use crate::error::Error;
2use crate::taskdef::{FromTaskArg, IntoTaskArg, TaskArg};
3use std::fs;
4use std::io::{self, Read, Seek, Write};
9use std::path::{Path, PathBuf};
10
11pub struct Blob(PathBuf);
12pub struct Output(pub(crate) PathBuf);
13
14impl Blob {
15 pub fn from_output(output: Output) -> Self {
16 Blob(output.0)
17 }
18
19 pub fn open(&self) -> io::Result<impl Read + Seek> {
20 fs::OpenOptions::new().read(true).open(&self.0)
21 }
22}
23
24impl Output {
25 pub fn open(&self) -> io::Result<impl Write + Seek> {
26 fs::OpenOptions::new()
27 .create(true)
28 .truncate(true)
29 .write(true)
30 .open(&self.0)
31 }
32
33 #[inline]
34 pub fn into_blob(self) -> Blob {
35 Blob::from_output(self)
36 }
37
38 pub fn file(self, path: &Path) -> Result<Blob, Error> {
39 let mut inf = fs::OpenOptions::new().read(true).open(path)?;
40 let mut outf = self.open()?;
41
42 io::copy(&mut inf, &mut outf)?;
43 Ok(Blob::from_output(self))
44 }
45
46 pub fn bytes(self, data: impl AsRef<[u8]>) -> Result<Blob, Error> {
47 let data = data.as_ref();
48 self.open()?.write_all(data)?;
49 Ok(Blob::from_output(self))
50 }
51}
52
53impl IntoTaskArg for Blob {
54 fn into_arg(&self, base: &Path) -> Result<TaskArg, Error> {
55 let cpath = em_canonicalize(&self.0)?;
56 let path = cpath.strip_prefix(base)?;
57 path.to_str()
58 .ok_or_else(|| Error::invalid_path(&self.0))
59 .map(|v| TaskArg::Blob(v.replace("\\", "/")))
60 }
61}
62
63#[cfg(target_arch = "wasm32")]
64fn em_canonicalize(path: &Path) -> io::Result<PathBuf> {
65 use std::path::Component;
66
67 let mut out_path = PathBuf::new();
68
69 for c in path.components() {
70 match c {
71 Component::ParentDir => {
72 let _ = out_path.pop();
73 }
74 Component::Normal(v) => out_path.push(v),
75 Component::RootDir => out_path.push("/"),
76 _ => (),
77 }
78 }
79
80 Ok(out_path)
81}
82
83#[cfg(not(target_arch = "wasm32"))]
84fn em_canonicalize(path: &Path) -> io::Result<PathBuf> {
85 path.canonicalize()
86}
87
88impl FromTaskArg for Blob {
89 fn from_arg(arg: TaskArg, base: &Path) -> Result<Self, Error> {
90 match arg {
91 TaskArg::Blob(path) => Ok(Blob(base.join(path))),
92 _ => Err(Error::BlobExpected),
93 }
94 }
95}
96
97impl IntoTaskArg for Output {
98 fn into_arg(&self, base: &Path) -> Result<TaskArg, Error> {
99 let path = self.0.strip_prefix(base)?;
100 path.to_str()
101 .ok_or_else(|| Error::invalid_path(&self.0))
102 .map(|v| TaskArg::Output(v.into()))
103 }
104}
105
106impl FromTaskArg for Output {
107 fn from_arg(arg: TaskArg, base: &Path) -> Result<Self, Error> {
108 Ok(match arg {
109 TaskArg::Output(path) => Output(PathBuf::from(&base.join(path))),
110 _ => return Err(Error::OutputExpected),
111 })
112 }
113}