cargo-build-dist 0.1.0

A cargo extension that helps with building distribution packages in various formats
Documentation
use std::process::Command;

use log::debug;

use crate::{Error, ErrorContext, Result};

pub fn is_current_target_runtime(target_runtime: &str) -> Result<bool> {
    let current_target_runtime = get_current_target_runtime()?;
    if target_runtime == current_target_runtime {
        debug!(
            "Current target runtime `{}` matches desired target runtime",
            target_runtime
        );
        Ok(true)
    } else {
        debug!(
            "Current target runtime `{}` does not match desired target runtime `{}`",
            current_target_runtime, target_runtime
        );
        Ok(false)
    }
}

pub fn get_current_target_runtime() -> Result<String> {
    let output = Command::new("rustc")
        .args(["--print", "cfg"])
        .output()
        .map_err(|err| {
            Error::new("failed to determine current Rust runtime target").with_source(err)
        })?
        .stdout;

    let output = String::from_utf8(output).unwrap();

    let mut arch = None;
    let mut vendor = None;
    let mut os = None;
    let mut env = None;

    for line in output.lines() {
        if let Some((key, value)) = line.split_once('=') {
            match key {
                "target_arch" => {
                    arch = Some(unquote(value).with_context("failed to unquote target_arch")?);
                }
                "target_vendor" => {
                    vendor = Some(unquote(value).with_context("failed to unquote target_vendor")?);
                }
                "target_os" => {
                    os = Some(unquote(value).with_context("failed to unquote target_os")?);
                }
                "target_env" => {
                    env = Some(unquote(value).with_context("failed to unquote target_env")?);
                }
                _ => (),
            }
        }
    }

    match (arch, vendor, os, env) {
        (Some(arch), Some(vendor), Some(os), Some(env)) => {
            let mut target = arch.to_string();

            target.push('-');
            target.push_str(vendor);
            target.push('-');
            target.push_str(os);
            target.push('-');
            target.push_str(env);

            Ok(target)
        }
        _ => Err(Error::new(
            "failed to determine current Rust runtime target",
        )),
    }
}

fn unquote(s: &str) -> Result<&str> {
    if s.starts_with('"') && s.ends_with('"') {
        Ok(&s[1..s.len() - 1])
    } else {
        Err(Error::new("failed to unquote string")
            .with_output(format!("s: {}", s))
            .with_explanation("The string was supposed to be quoted but it wasn't."))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_current_target_runtime() {
        assert!(get_current_target_runtime().is_ok());
    }

    #[test]
    fn test_unquote() {
        assert_eq!(unquote("\"foo\"").unwrap(), "foo");
        assert_eq!(unquote("\"f o o\"").unwrap(), "f o o");

        unquote("\"foo").unwrap_err();
        unquote("foo\"").unwrap_err();
        unquote("foo").unwrap_err();
    }
}