use anyhow::Context as _;
use petgraph::algo::toposort;
use petgraph::graph::DiGraph;
use serde::Deserialize;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use toml::Value;
#[derive(Debug, Deserialize, Clone)]
pub struct TargetConfig
{
pub target: Option<String>,
pub format: String,
pub flags: Option<String>,
pub inherit: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, Value>,
pub package: Option<String>,
pub binaries: Option<Vec<String>>,
pub files: Option<Vec<(String, String, String)>>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct DistConfig
{
pub targets: HashMap<String, TargetConfig>,
}
impl DistConfig
{
pub fn load(path: &Path) -> anyhow::Result<Self>
{
let content = std::fs::read_to_string(path)?;
let config: DistConfig = toml::from_str(&content)?;
Ok(config)
}
pub fn find() -> anyhow::Result<PathBuf>
{
let candidates = [
Path::new(".distinfo/targets.toml"),
Path::new("dist-targets.toml"),
Path::new(".cargo/distinfo.toml"),
];
for candidate in candidates
{
if candidate.exists()
{
return Ok(candidate.to_path_buf());
}
}
anyhow::bail!("`.distinfo/targets.toml` not found.")
}
}
#[derive(Debug, Clone)]
pub struct ResolvedTarget
{
pub name: String,
pub target: String,
pub format: String,
pub flags: Option<String>,
pub extra: HashMap<String, Value>,
pub package: Option<String>,
pub binaries: Option<Vec<String>>,
pub files: Option<Vec<(String, String, String)>>,
}
pub fn resolve_targets(
raw: HashMap<String, TargetConfig>,
) -> anyhow::Result<HashMap<String, ResolvedTarget>>
{
let mut graph = DiGraph::<String, ()>::new();
let mut node_indices = HashMap::new();
for name in raw.keys()
{
let idx = graph.add_node(name.clone());
node_indices.insert(name.clone(), idx);
}
for (name, config) in &raw
{
if let Some(parent) = &config.inherit
&& let Some(&parent_idx) = node_indices.get(parent)
&& let Some(&child_idx) = node_indices.get(name)
{
graph.add_edge(parent_idx, child_idx, ());
}
}
let sorted = toposort(&graph, None)
.map_err(|_| anyhow::anyhow!("Inheritance loop detected."))?;
let mut resolved: HashMap<String, ResolvedTarget> = HashMap::new();
for node in sorted
{
let name = &graph[node];
let config =
raw.get(name).context("Can't obtain target cofniguration")?;
let mut resolved_config = ResolvedTarget {
name: name.clone(),
target: config.target.clone().unwrap_or_default(),
format: config.format.clone(),
flags: config.flags.clone(),
extra: config.extra.clone(),
package: config.package.clone(),
binaries: config.binaries.clone(),
files: config.files.clone(),
};
if let Some(parent) = &config.inherit
{
if let Some(parent_resolved) = resolved.get(parent)
{
if resolved_config.target.is_empty()
{
resolved_config.target = parent_resolved.target.clone();
}
if resolved_config.flags.is_none()
{
resolved_config.flags = parent_resolved.flags.clone();
}
for (k, v) in &parent_resolved.extra
{
resolved_config.extra.entry(k.clone()).or_insert(v.clone());
}
}
else
{
anyhow::bail!("'{}' base target not found.", parent);
}
}
if resolved_config.target.is_empty()
{
anyhow::bail!(
"'{}' target have not 'target' and doesn't inherit it.",
name
);
}
resolved.insert(name.clone(), resolved_config);
}
Ok(resolved)
}