//! Closed constructor payload bindings. The core owns decisions and review scope; //! this adapter supplies complete target records for its Candidate agreement check. use super::{scalar, *}; pub(super) fn selectors() -> [FacetSelector; 5] { [ FacetSelector::Data(DataFacet::Constructor), FacetSelector::Port(PortFacet::Constructor), FacetSelector::Occurrence(OccurrenceFacet::Constructor), FacetSelector::Occurrence(OccurrenceFacet::MessageKind), FacetSelector::Event(EventFacet::Constructor), ] } pub(super) fn roots(project: &Project, target: Id, facet: &FacetSelector) -> Option> { let root = scalar::binding(project, target, facet)?; let mut roots = vec![root.clone()]; match facet { FacetSelector::Data(DataFacet::Constructor) => { roots.push(format!("{}/default", root.strip_suffix("/definition")?)); } FacetSelector::Port(PortFacet::Constructor) => { roots.push(format!("{}/legacy", root.strip_suffix("/contract")?)); } _ => {} } Some(roots) } pub(super) fn available(project: &Project, trace: &Trace, facet: &FacetSelector) -> bool { roots(project, trace.oa, facet).is_some() && roots(project, trace.sa, facet).is_some() } /// Used for explanation and explicit overlap controls, never for acknowledgment. pub(super) fn contains(group: &FacetSelector, facet: &FacetSelector) -> bool { use FacetSelector as F; match group { F::Data(DataFacet::Constructor) => { matches!(facet, F::Data(f) if *f != DataFacet::Unit) || matches!(facet, F::Literal(_)) } F::Port(PortFacet::Constructor) => matches!( facet, F::Port(_) | F::Member { collection: ReferenceCollection::FunctionInputItems | ReferenceCollection::FunctionOutputItems, .. } ), F::Occurrence(OccurrenceFacet::Constructor) => { matches!(facet, F::Occurrence(f) if !matches!(f, OccurrenceFacet::Precondition | OccurrenceFacet::Postcondition)) || matches!( facet, F::Annotation(_) | F::Binding(_) | F::Member { collection: ReferenceCollection::Steps | ReferenceCollection::FragmentBranches, .. } | F::Order(_) ) } F::Occurrence(OccurrenceFacet::MessageKind) => matches!( facet, F::Occurrence(OccurrenceFacet::MessageKind | OccurrenceFacet::TimerExpression) ), F::Event(EventFacet::Constructor) => matches!( facet, F::Event(_) | F::Member { collection: ReferenceCollection::SignalItems, .. } ), _ => false, } } pub(super) fn description(facet: &FacetSelector) -> &'static str { match facet { FacetSelector::Data(_) => { "Definition, bounds or literal members, record supertype and default. Unit stays independent." } FacetSelector::Port(_) => { "Contract, item members and retained generic association. Owner stays independent." } FacetSelector::Occurrence(OccurrenceFacet::Constructor) => { "Occurrence body, including references, message annotations, bindings and branch or step order. Pre/postconditions stay independent." } FacetSelector::Occurrence(_) => { "Message kind and Timer Expression. Exchange references and annotations stay independent." } FacetSelector::Event(_) => { "Event body, including signal items or its complete time/change expression." } _ => "", } } fn chosen(keys: &KeyPair, layer: Layer) -> Option<&T> { match layer { Layer::Oa => keys.oa.as_ref(), Layer::Sa => keys.sa.as_ref(), } } // The source payload is mapped by the core's typed visitor, never by replacing // UUID-looking strings. For a historical same-kind Take, retain only current // target-present/source-absent rows with no prior source-present observation. The final core planner // checks this assembled record against its own independent expected result. pub(super) fn take_value( project: &Project, trace: &Trace, direction: TransitionDirection, facet: &FacetSelector, map: &BTreeMap, ) -> Result { let (source, target) = if direction == TransitionDirection::OaToSa { (trace.oa, trace.sa) } else { (trace.sa, trace.oa) }; let mut element = project .elements .get(&source) .cloned() .ok_or("Missing constructor source.")?; if !message_kind::handles(facet) { element.parent = None; let mut missing = BTreeSet::new(); oa_core::references::visit_element_refs(&mut element, |id| { if let Some(mapped) = map.get(id) { *id = *mapped; } else { missing.insert(*id); } }); if !missing.is_empty() { return Err(format!( "Missing explicit reference correspondence: {missing:?}" )); } } let supplied = serde_json::to_value(element).map_err(|e| e.to_string())?; let mut value = Draft::from_project(project, target) .ok_or("Missing target draft.")? .value; for (from, to) in roots(project, source, facet) .ok_or("Source group is unavailable.")? .iter() .zip(roots(project, target, facet).ok_or("Target group is unavailable.")?) { *value .pointer_mut(&to) .ok_or("Target group payload is unavailable.")? = supplied .pointer(from) .cloned() .ok_or("Source group payload is unavailable.")?; } let current = capture_facet(project, trace.oa, trace.sa, facet)?; if scalar::observation(¤t, Layer::Oa) != scalar::observation(¤t, Layer::Sa) { return Ok(value); } let references = if direction == TransitionDirection::OaToSa { map.clone() } else { map.iter().map(|(sa, oa)| (*oa, *sa)).collect() }; let old = trace.baseline.as_ref().and_then(|b| b.semantic.as_ref()); for fact in semantic_facets(project, trace, &references)? { if !contains(facet, &fact.selector) { continue; } let Some((path, key)) = target_row(project, target, &fact.selector, direction.target_layer()) else { continue; }; if row_present(&fact.current, direction.source_layer()) || !row_present(&fact.current, direction.target_layer()) { continue; } let was_paired = old.is_some_and(|b| { b.facets.iter().any(|previous| { row_present(previous, direction.source_layer()) && target_row( project, target, &previous.selector(), direction.target_layer(), ) .as_ref() == Some(&(path.clone(), key.clone())) }) }); if was_paired { continue; } let row = scalar::value(&fact.current, direction.target_layer()) .ok_or("Target row is inapplicable.")?; let items = value .pointer_mut(&path) .and_then(Value::as_array_mut) .ok_or("Target collection is unavailable.")?; match &fact.selector { FacetSelector::Member { .. } | FacetSelector::Literal(_) => { if row == Value::Bool(true) && !items.contains(&key) { items.push(key); } else if row == Value::Bool(false) { items.retain(|v| v != &key); } } FacetSelector::Annotation(_) => { items.retain(|v| { serde_json::from_value::(v.clone()) .ok() .map(|v| serde_json::to_value(AnnotationKey::from(&v)).unwrap()) .as_ref() != Some(&key) }); if !row.is_null() { items.push(row); } } FacetSelector::Binding(_) => { items.retain(|v| v.get("referenced") != Some(&key)); if !row.is_null() { items.push(row); } } _ => unreachable!(), } } Ok(value) } fn row_present(fact: &FacetReview, layer: Layer) -> bool { match fact { FacetReview::Member { .. } | FacetReview::Literal { .. } => { scalar::value(fact, layer) == Some(Value::Bool(true)) } FacetReview::Annotation { .. } | FacetReview::Binding { .. } => { scalar::value(fact, layer).is_some_and(|value| !value.is_null()) } _ => false, } } fn row_key(facet: &FacetSelector, layer: Layer) -> Option { match facet { FacetSelector::Member { keys, .. } | FacetSelector::Binding(keys) => { chosen(keys, layer).map(|v| serde_json::json!(v)) } FacetSelector::Literal(keys) => chosen(keys, layer).map(|v| serde_json::json!(v)), FacetSelector::Annotation(keys) => chosen(keys, layer).map(|v| serde_json::json!(v)), _ => None, } } fn target_row( project: &Project, target: Id, facet: &FacetSelector, layer: Layer, ) -> Option<(String, Value)> { let key = row_key(facet, layer)?; let (group, suffix) = match facet { FacetSelector::Literal(_) => ( FacetSelector::Data(DataFacet::Constructor), "/Enumeration/literals", ), FacetSelector::Binding(_) => ( FacetSelector::Occurrence(OccurrenceFacet::Constructor), "/ScenarioUse/bindings", ), FacetSelector::Annotation(_) => ( FacetSelector::Occurrence(OccurrenceFacet::Constructor), "/Exchange/values", ), FacetSelector::Member { collection, .. } => match collection { ReferenceCollection::FunctionInputItems => ( FacetSelector::Port(PortFacet::Constructor), "/FunctionInput/incoming_items", ), ReferenceCollection::FunctionOutputItems => ( FacetSelector::Port(PortFacet::Constructor), "/FunctionOutput/outgoing_items", ), ReferenceCollection::SignalItems => ( FacetSelector::Event(EventFacet::Constructor), "/Signal/items", ), ReferenceCollection::Steps => ( FacetSelector::Occurrence(OccurrenceFacet::Constructor), "/Branch/occurrences", ), ReferenceCollection::FragmentBranches => ( FacetSelector::Occurrence(OccurrenceFacet::Constructor), "/Fragment/branches", ), _ => return None, }, _ => return None, }; Some(( format!("{}{suffix}", scalar::binding(project, target, &group)?), key, )) } /// A selected descendant may fill untouched group fields during first-equal /// acknowledgment, but cannot silently replace an authored group field. pub(super) fn authored_agrees(original: &Value, authored: &Value, final_value: &Value) -> bool { if original == authored || authored == final_value { return true; } match (original, authored, final_value) { (Value::Object(a), Value::Object(b), Value::Object(c)) if a.keys().eq(b.keys()) => { b.iter().all(|(key, v)| { c.get(key) .is_some_and(|final_value| authored_agrees(&a[key], v, final_value)) }) } _ => false, } }