use crate::pipeline::*;
use std::any::Any;
use std::hash::Hash;
#[derive(Clone)]
pub(crate) struct Attached {
pub(crate) name: String,
pub(crate) value: Value,
pub(crate) prov: Prov,
}
pub(crate) struct Attachments<K> {
committed: HashMap<K, Vec<Attached>>,
pending: Vec<(K, Attached)>,
}
impl<K> Default for Attachments<K> {
fn default() -> Self {
Attachments {
committed: HashMap::new(),
pending: Vec::new(),
}
}
}
fn with_attachments<K: Hash + Eq + Clone + 'static, R>(
f: impl FnOnce(&mut Attachments<K>) -> R,
) -> R {
with_run(|run| {
let slot: &mut Box<dyn Any> = run
.attached
.get_or_insert_with(|| Box::new(Attachments::<K>::default()));
match slot.downcast_mut::<Attachments<K>>() {
Some(a) => f(a),
None => f(&mut Attachments::<K>::default()),
}
})
}
pub(crate) fn attached<K: Hash + Eq + Clone + 'static>(
key: &K,
name: &str,
) -> Option<(Value, Prov)> {
with_attachments::<K, _>(|a| {
a.committed
.get(key)?
.iter()
.find(|r| r.name == name)
.map(|r| (r.value.clone(), r.prov.clone()))
})
}
pub(crate) fn attached_all<K: Hash + Eq + Clone + 'static>(key: &K) -> Vec<Attached> {
with_attachments::<K, _>(|a| a.committed.get(key).cloned().unwrap_or_default())
}
pub(crate) fn attached_fields<K: Hash + Eq + Clone + 'static>(key: &K) -> Vec<(String, Value)> {
attached_all(key)
.into_iter()
.map(|r| (r.name, r.value))
.collect()
}
pub(crate) fn any_attached<K: Hash + Eq + Clone + 'static>() -> bool {
with_attachments::<K, _>(|a| !a.committed.is_empty())
}
pub(crate) fn attach<K: Hash + Eq + Clone + 'static>(key: K, regula: Attached) {
with_attachments::<K, _>(|a| a.pending.push((key, regula)));
}
pub(crate) fn commit<K: Hash + Eq + Clone + 'static>() -> std::result::Result<bool, String> {
with_attachments::<K, _>(|a| {
let pending = std::mem::take(&mut a.pending);
let mut seen: HashMap<(&K, &str), &Value> = HashMap::new();
for (key, r) in &pending {
match seen.get(&(key, r.name.as_str())) {
Some(v) if **v != r.value => {
return Err(format!(
"threads standing at one node attach differing values under '{}' ({} and {}): \
'@| collapse' first, so that one thread stands at each node and attaches the gathered value",
r.name,
v.display_form(),
r.value.display_form()
));
}
Some(_) => {}
None => {
seen.insert((key, r.name.as_str()), &r.value);
}
}
}
let mut changed = false;
for (key, r) in pending {
let at = a.committed.entry(key).or_default();
match at.iter_mut().find(|x| x.name == r.name) {
Some(x) => {
if x.value != r.value {
changed = true;
}
*x = r;
}
None => {
changed = true;
at.push(r);
}
}
}
Ok(changed)
})
}