pipecircuit-cli 0.0.14

Command-line architectural tools for Pipecircuit programs
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",
        ))
    }
}