use std::{
io::{self, Write as _},
path::Path,
process::{Command, Stdio},
};
use super::{ArchError, model::Architecture};
pub(super) fn document(architecture: &Architecture) -> Result<String, ArchError> {
let data = serde_json::to_string(architecture)
.map_err(ArchError::Json)?
.replace('&', "\\u0026")
.replace('<', "\\u003c")
.replace('>', "\\u003e");
Ok(include_str!("browser.html").replace("__ARCHITECTURE_DATA__", &data))
}
pub(super) fn present(html: &str) -> Result<String, ArchError> {
let mut file = tempfile::Builder::new()
.prefix("pipec-architecture-")
.suffix(".html")
.tempfile()
.map_err(|error| ArchError::Write(std::env::temp_dir(), error))?;
file.write_all(html.as_bytes())
.map_err(|error| ArchError::Write(file.path().to_owned(), error))?;
let (file, path) = file
.keep()
.map_err(|error| ArchError::Write(error.file.path().to_owned(), error.error))?;
drop(file);
let launch = match launch(&path) {
Ok(()) => "Requested your browser to open the source map.".to_owned(),
Err(error) => {
format!("Could not launch a browser: {error}. Open the saved HTML file manually.")
}
};
Ok(format!(
"{launch}\nSaved view: {}\nThis is a local snapshot; rerun the command after code changes.\n",
path.display()
))
}
fn launch(path: &Path) -> io::Result<()> {
let mut command = launcher()?;
command
.arg(path)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
Ok(())
}
fn launcher() -> io::Result<Command> {
if cfg!(target_os = "macos") {
Ok(Command::new("open"))
} else if cfg!(target_os = "windows") {
let mut command = Command::new("rundll32.exe");
command.arg("url.dll,FileProtocolHandler");
Ok(command)
} else if cfg!(unix) {
Ok(Command::new("xdg-open"))
} else {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"no browser launcher for this platform",
))
}
}