use std::{
io::{self, BufReader, Read, Write},
path::{Path, PathBuf},
process::Child,
time::Duration,
};
use serde::de::DeserializeOwned;
use crate::wasm_runtime::DeserializationFmt;
pub fn deserialize<T, R>(deser_format: Option<DeserializationFmt>, data: &R) -> anyhow::Result<T>
where
T: DeserializeOwned,
R: AsRef<[u8]> + ?Sized,
{
match deser_format {
Some(DeserializationFmt::Json) => {
let deser = serde_json::from_slice(data.as_ref())?;
Ok(deser)
}
_ => Ok(bincode::deserialize(data.as_ref())?),
}
}
pub(crate) fn pipe_std_streams(mut child: Child) -> anyhow::Result<()> {
let c_stdout = child.stdout.take().expect("Failed to open command stdout");
let c_stderr = child.stderr.take().expect("Failed to open command stderr");
let write_child_stderr = move || -> anyhow::Result<()> {
let mut stderr = io::stderr();
let mut reader = BufReader::new(c_stderr);
let mut buffer = [0; 1024];
while let Ok(n) = reader.read(&mut buffer) {
if n == 0 {
break;
}
stderr.write_all(&buffer[..n])?;
}
Ok(())
};
let write_child_stdout = move || -> anyhow::Result<()> {
let mut stdout = io::stdout();
let mut reader = BufReader::new(c_stdout);
let mut buffer = [0; 1024];
while let Ok(n) = reader.read(&mut buffer) {
if n == 0 {
break;
}
stdout.write_all(&buffer[..n])?;
}
Ok(())
};
std::thread::spawn(write_child_stdout);
std::thread::spawn(write_child_stderr);
loop {
match child.try_wait() {
Ok(Some(status)) => {
if !status.success() {
anyhow::bail!("exit with status: {status}");
}
break;
}
Ok(None) => {
std::thread::sleep(Duration::from_millis(500));
}
Err(err) => {
return Err(err.into());
}
}
}
Ok(())
}
pub fn get_workspace_target_dir(crate_dir: &Path) -> PathBuf {
const TARGET_DIR_VAR: &str = "CARGO_TARGET_DIR";
resolve_target_dir(std::env::var(TARGET_DIR_VAR).ok(), crate_dir)
}
fn resolve_target_dir(env_target_dir: Option<String>, crate_dir: &Path) -> PathBuf {
if let Some(dir) = env_target_dir {
return PathBuf::from(dir);
}
find_workspace_root_from(crate_dir)
.unwrap_or_else(|| crate_dir.to_path_buf())
.join("target")
}
fn find_workspace_root_from(start_path: &Path) -> Option<PathBuf> {
start_path
.ancestors()
.find(|p| {
p.join("Cargo.toml").exists() && {
let content = std::fs::read_to_string(p.join("Cargo.toml")).unwrap_or_default();
content.contains("[workspace]")
}
})
.map(Path::to_path_buf)
}
#[cfg(test)]
mod tests {
use super::*;
fn crate_at(dir: &Path, manifest: &str) {
std::fs::create_dir_all(dir).unwrap();
std::fs::write(dir.join("Cargo.toml"), manifest).unwrap();
}
#[test]
fn resolves_the_workspace_of_the_crate_being_built_not_fdevs_own() {
let tmp = tempfile::tempdir().unwrap();
let ws = tmp.path().join("ws");
let member = ws.join("member");
crate_at(
&ws,
"[workspace]\nmembers = [\"member\"]\nresolver = \"2\"\n",
);
crate_at(
&member,
"[package]\nname = \"member\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
);
assert_eq!(
resolve_target_dir(None, &member),
ws.join("target"),
"a workspace member must resolve to ITS workspace's target dir"
);
}
#[test]
fn a_crate_in_no_workspace_gets_its_own_target_dir_and_does_not_panic() {
let tmp = tempfile::tempdir().unwrap();
let solo = tmp.path().join("solo");
crate_at(
&solo,
"[package]\nname = \"solo\"\nversion = \"0.1.0\"\nedition = \"2021\"\n[workspace]\n",
);
assert_eq!(resolve_target_dir(None, &solo), solo.join("target"));
}
#[test]
fn an_explicit_target_dir_wins_over_the_search() {
let tmp = tempfile::tempdir().unwrap();
let ws = tmp.path().join("ws");
let member = ws.join("member");
crate_at(&ws, "[workspace]\nmembers = [\"member\"]\n");
crate_at(
&member,
"[package]\nname = \"member\"\nversion = \"0.1.0\"\n",
);
assert_eq!(
resolve_target_dir(Some("/explicit/target".to_string()), &member),
PathBuf::from("/explicit/target"),
"an explicit CARGO_TARGET_DIR must not be second-guessed"
);
}
#[test]
fn find_workspace_root_from_returns_none_when_there_is_no_workspace() {
let tmp = tempfile::tempdir().unwrap();
let lone = tmp.path().join("a/b/c");
std::fs::create_dir_all(&lone).unwrap();
let found = find_workspace_root_from(&lone);
assert!(
found.is_none() || !found.as_deref().unwrap().starts_with(tmp.path()),
"no workspace exists inside the temp tree, so none may be reported from it"
);
}
}