Skip to content

Commit

Permalink
Implement Cross.toml deserialization using serde
Browse files Browse the repository at this point in the history
  • Loading branch information
mntns committed Mar 22, 2022
1 parent a11c252 commit e53c99f
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 175 deletions.
50 changes: 50 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ rustc_version = "0.4"
toml = "0.5"
which = { version = "4", default_features = false }
shell-escape = "0.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[target.'cfg(not(windows))'.dependencies]
Expand Down
29 changes: 12 additions & 17 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Result, Target, Toml};
use crate::{Result, Target, CrossToml};

use crate::errors::*;
use std::collections::HashMap;
Expand Down Expand Up @@ -101,27 +101,27 @@ fn split_to_cloned_by_ws(string: &str) -> Vec<String> {

#[derive(Debug)]
pub struct Config {
toml: Option<Toml>,
toml: Option<CrossToml>,
env: Environment,
}

impl Config {
pub fn new(toml: Option<Toml>) -> Self {
pub fn new(toml: Option<CrossToml>) -> Self {
Config {
toml,
env: Environment::new(None),
}
}

#[cfg(test)]
fn new_with(toml: Option<Toml>, env: Environment) -> Self {
fn new_with(toml: Option<CrossToml>, env: Environment) -> Self {
Config { toml, env }
}

pub fn xargo(&self, target: &Target) -> Result<Option<bool>> {
let (build_xargo, target_xargo) = self.env.xargo(target)?;
let (toml_build_xargo, toml_target_xargo) = if let Some(ref toml) = self.toml {
toml.xargo(target)?
toml.xargo(target)
} else {
(None, None)
};
Expand All @@ -145,15 +145,15 @@ impl Config {
if let Some(env_value) = env_value {
return Ok(Some(env_value));
}
self.toml.as_ref().map_or(Ok(None), |t| t.image(target))
self.toml.as_ref().map_or(Ok(None), |t| Ok(t.image(target)))
}

pub fn runner(&self, target: &Target) -> Result<Option<String>> {
let env_value = self.env.runner(target);
if let Some(env_value) = env_value {
return Ok(Some(env_value));
}
self.toml.as_ref().map_or(Ok(None), |t| t.runner(target))
self.toml.as_ref().map_or(Ok(None), |t| Ok(t.runner(target)))
}

pub fn env_passthrough(&self, target: &Target) -> Result<Vec<String>> {
Expand All @@ -179,15 +179,15 @@ impl Config {
Ok(collected)
}

fn sum_of_env_toml_values<'a>(
toml_getter: impl FnOnce() -> Option<Result<Vec<&'a str>>>,
fn sum_of_env_toml_values(
toml_getter: impl FnOnce() -> Option<Vec<String>>,
env_values: Option<Vec<String>>,
) -> Result<Vec<String>> {
let mut collect = vec![];
if let Some(mut vars) = env_values {
collect.append(&mut vars);
} else if let Some(toml_values) = toml_getter() {
collect.extend(toml_values?.into_iter().map(|v| v.to_string()));
collect.extend(toml_values.into_iter().map(|v| v.to_string()));
}

Ok(collect)
Expand Down Expand Up @@ -277,13 +277,8 @@ mod tests {
use super::*;
use std::matches;

fn toml(content: &str) -> Result<crate::Toml> {
Ok(crate::Toml {
table: match content.parse().wrap_err("couldn't parse toml")? {
toml::Value::Table(table) => table,
_ => eyre::bail!("couldn't parse toml as TOML table"),
},
})
fn toml(content: &str) -> Result<crate::CrossToml> {
Ok(CrossToml::from_str(content).wrap_err("couldn't parse toml")?)
}

#[test]
Expand Down
185 changes: 185 additions & 0 deletions src/cross_toml.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
use std::collections::HashMap;
use serde::Deserialize;
use crate::errors::*;
use crate::Target;

/// Build environment configuration
#[derive(Debug, Deserialize, PartialEq)]
pub struct CrossBuildEnvConfig {
volumes: Option<Vec<String>>,
passthrough: Option<Vec<String>>,
}

/// Build configuration
#[derive(Debug, Deserialize, PartialEq)]
pub struct CrossBuildConfig {
env: Option<CrossBuildEnvConfig>,
xargo: Option<bool>,
}

/// Target configuration
#[derive(Debug, Deserialize, PartialEq)]
pub struct CrossTargetConfig {
passthrough: Option<Vec<String>>,
volumes: Option<Vec<String>>,
xargo: Option<bool>,
image: Option<String>,
runner: Option<String>,
}

/// Wrapper struct for `Target` -> `CrossTargetConfig` mappings
///
/// This is used to circumvent that serde's flatten and field aliases
/// currently don't work together: https://github.com/serde-rs/serde/issues/1504
#[derive(Debug, Deserialize, PartialEq)]
pub struct CrossTargetMapConfig {
#[serde(flatten)]
inner: HashMap<Target, CrossTargetConfig>,
}

/// Cross configuration
#[derive(Debug, Deserialize, PartialEq)]
pub struct CrossToml {
#[serde(rename = "target")]
targets: Option<CrossTargetMapConfig>,
build: Option<CrossBuildConfig>,
}

impl CrossToml {
/// Parses the `CrossConfig` from a string
pub fn from_str(toml_str: &str) -> Result<Self> {
let cfg: CrossToml = toml::from_str(toml_str)?;
Ok(cfg)
}

/// Returns the `target.{}.image` part of `Cross.toml`
pub fn image(&self, target: &Target) -> Option<String> {
self.get_target(target).and_then(|t| t.image.clone())
}

/// Returns the `target.{}.runner` part of `Cross.toml`
pub fn runner(&self, target: &Target) -> Option<String> {
self.get_target(target).and_then(|t| t.runner.clone())
}

/// Returns the `build.xargo` or the `target.{}.xargo` part of `Cross.toml`
pub fn xargo(&self, target: &Target) -> (Option<bool>, Option<bool>) {
let build_xargo = self.build.as_ref().and_then(|b| b.xargo);
let target_xargo = self.get_target(target).and_then(|t| t.xargo);

(build_xargo, target_xargo)
}

/// Returns the list of environment variables to pass through for `build`,
pub fn env_passthrough_build(&self) -> Vec<String> {
self.get_build_env()
.and_then(|e| e.passthrough.as_ref())
.map_or(Vec::new(), |v| v.to_vec())
}

/// Returns the list of environment variables to pass through for `target`,
pub fn env_passthrough_target(&self, target: &Target) -> Vec<String> {
self.get_target(target)
.and_then(|t| t.passthrough.as_ref())
.map_or(Vec::new(), |v| v.to_vec())
}

/// Returns the list of environment variables to pass through for `build`,
pub fn env_volumes_build(&self) -> Vec<String> {
self.get_build_env()
.and_then(|e| e.volumes.as_ref())
.map_or(Vec::new(), |v| v.to_vec())
}

/// Returns the list of environment variables to pass through for `target`,
pub fn env_volumes_target(&self, target: &Target) -> Vec<String> {
self.get_target(target)
.and_then(|t| t.volumes.as_ref())
.map_or(Vec::new(), |v| v.to_vec())
}

/// Returns a reference to the `CrossTargetConfig` of a specific `target`
fn get_target(&self, target: &Target) -> Option<&CrossTargetConfig> {
self.targets.as_ref().and_then(|t| t.inner.get(target))
}

/// Returns a reference to the `CrossBuildEnvConfig`
fn get_build_env(&self) -> Option<&CrossBuildEnvConfig> {
self.build.as_ref().and_then(|b| b.env.as_ref())
}
}

mod tests {
use super::*;

#[test]
pub fn parse_empty_toml() -> Result<()> {
let cfg = CrossToml { targets: None, build: None };
let parsed_cfg = CrossToml::from_str("")?;

assert_eq!(parsed_cfg, cfg);

Ok(())
}

#[test]
pub fn parse_build_toml() -> Result<()> {
let cfg = CrossToml {
targets: None,
build: Some(CrossBuildConfig {
env: Some(CrossBuildEnvConfig {
volumes: Some(vec!["vol1".to_string(), "vol2".to_string()]),
passthrough: Some(vec!["VAR1".to_string(), "VAR2".to_string()])
}),
xargo: Some(true),
})
};

let test_str = r#"
[build]
xargo = true
[build.env]
volumes = ["vol1", "vol2"]
passthrough = ["VAR1", "VAR2"]
"#;
let parsed_cfg = CrossToml::from_str(test_str)?;

assert_eq!(parsed_cfg, cfg);

Ok(())
}

#[test]
pub fn parse_target_toml() -> Result<()> {
let mut target_map = HashMap::new();
target_map.insert(
Target::BuiltIn { triple: "aarch64-unknown-linux-gnu".to_string() },
CrossTargetConfig {
passthrough: Some(vec!["VAR1".to_string(), "VAR2".to_string()]),
volumes: Some(vec!["vol1".to_string(), "vol2".to_string()]),
xargo: Some(false),
image: Some("test-image".to_string()),
runner: None,
}
);

let cfg = CrossToml {
targets: Some(CrossTargetMapConfig { inner: target_map }),
build: None,
};

let test_str = r#"
[target.aarch64-unknown-linux-gnu]
volumes = ["vol1", "vol2"]
passthrough = ["VAR1", "VAR2"]
xargo = false
image = "test-image"
"#;
let parsed_cfg = CrossToml::from_str(test_str)?;

assert_eq!(parsed_cfg, cfg);

Ok(())
}
}
Loading

0 comments on commit e53c99f

Please sign in to comment.