use std::cell::RefCell;
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::rc::Rc;
#[derive(Debug, Default, Clone)]
pub struct Frame {
pub lane: String,
pub params: BTreeMap<String, String>,
pub env: BTreeMap<String, String>,
pub workdir: PathBuf,
pub dry_run: bool,
}
impl Frame {
pub fn meta(&self) -> BTreeMap<String, String> {
BTreeMap::from([
("lane".to_string(), self.lane.clone()),
("version".to_string(), env!("CARGO_PKG_VERSION").to_string()),
])
}
}
pub type SharedFrame = Rc<RefCell<Frame>>;
#[derive(Debug, Default)]
pub struct Outputs {
values: BTreeMap<String, BTreeMap<String, String>>,
}
impl Outputs {
pub fn set(&mut self, id: &str, key: &str, value: impl Into<String>) {
self.values
.entry(id.to_string())
.or_default()
.insert(key.to_string(), value.into());
}
pub fn get(&self, id: &str, key: &str) -> Option<&String> {
self.values.get(id)?.get(key)
}
pub fn flatten(&self) -> BTreeMap<String, String> {
let mut flat = BTreeMap::new();
for (id, entries) in &self.values {
for (key, value) in entries {
flat.insert(format!("{id}.{key}"), value.clone());
}
}
flat
}
}
pub type SharedOutputs = Rc<RefCell<Outputs>>;
pub fn parse_params<I, S>(args: I) -> BTreeMap<String, String>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
args.into_iter()
.filter_map(|arg| {
arg.as_ref()
.split_once('=')
.map(|(k, v)| (k.to_string(), v.to_string()))
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_key_value_pairs() {
let params = parse_params(["target=production", "notes=hello"]);
assert_eq!(params.get("target").map(String::as_str), Some("production"));
assert_eq!(params.get("notes").map(String::as_str), Some("hello"));
}
#[test]
fn splits_on_the_first_equals_only() {
let params = parse_params(["query=a=b"]);
assert_eq!(params.get("query").map(String::as_str), Some("a=b"));
}
#[test]
fn ignores_arguments_without_an_equals() {
let params = parse_params(["standalone"]);
assert!(params.is_empty());
}
#[test]
fn meta_exposes_the_lane_and_version() {
let frame = Frame {
lane: "beta".to_string(),
..Frame::default()
};
let meta = frame.meta();
assert_eq!(meta.get("lane").map(String::as_str), Some("beta"));
assert!(meta.contains_key("version"));
}
#[test]
fn outputs_are_looked_up_by_step_and_key() {
let mut outputs = Outputs::default();
outputs.set("build", "stdout", "ok");
assert_eq!(
outputs.get("build", "stdout").map(String::as_str),
Some("ok")
);
assert_eq!(outputs.get("build", "missing"), None);
assert_eq!(outputs.get("nope", "stdout"), None);
}
#[test]
fn outputs_flatten_for_interpolation() {
let mut outputs = Outputs::default();
outputs.set("build", "stdout", "ok");
outputs.set("build", "code", "0");
let flat = outputs.flatten();
assert_eq!(flat.get("build.stdout").map(String::as_str), Some("ok"));
assert_eq!(flat.get("build.code").map(String::as_str), Some("0"));
}
}
#[derive(Debug, Clone)]
pub struct Cleanup {
pub what: String,
pub command: String,
}
pub type SharedCleanups = Rc<RefCell<Vec<Cleanup>>>;