//! Existing-core expressivity experiment, not a proposed production DTO. use oa_core::validation::{Severity, validate}; use oa_core::*; use std::{collections::BTreeMap, fs, path::Path}; fn id(n: u128) -> Id { Id::from_u128(0x52060906000040008000000000000000 + n) } fn add(p: &mut Project, n: u128, k: Kind, name: &str, parent: Option) -> Id { let mut e = if k == Kind::Port { Element { id: id(n), alias: format!("operation_probe_{n}"), name: name.into(), description: String::new(), parent, body: ElementBody::shared_in( Layer::Sa, SharedBody::Port { contract: PortContract::Component { kind: ComponentPortKind::Standard, orientation: ComponentPortOrientation::Unset, }, legacy: None, }, ), } } else { p.make_element(k, Layer::Sa, name, parent).unwrap() }; e.id = id(n); e.alias = format!("operation_probe_{n}"); p.elements.insert(e.id, e); id(n) } fn body(p: &mut Project, n: u128, b: SharedBody) { *p.elements.get_mut(&id(n)).unwrap().shared_mut().unwrap() = b; } fn rel(p: &mut Project, n: u128, k: RelationKind, a: u128, b: u128) { p.relations.insert( id(n), Relation { id: id(n), alias: format!("operation_probe_{n}"), name: format!("{k:?} {n}"), description: String::new(), body: RelationBody::new(k, id(a), id(b)), }, ); } fn valid(p: &Project) { let errors: Vec<_> = validate(p) .into_iter() .filter(|i| i.severity == Severity::Error) .collect(); assert!(errors.is_empty(), "{errors:?}"); } fn message(p: &Project, n: u128) -> &OccurrenceBody { let Some(SharedBody::Occurrence { body, .. }) = p.elements[&id(n)].shared() else { panic!() }; body } fn set_exchange(p: &mut Project, n: u128, target: Id) { let Some(SharedBody::Occurrence { body: OccurrenceBody::Exchange { exchange, .. }, .. }) = p.elements.get_mut(&id(n)).unwrap().shared_mut() else { panic!() }; *exchange = Some(target); } fn initial() -> Project { let mut p = Project::empty(); p.id = id(1); p.name = "Two operations, one interface".into(); add(&mut p, 10, Kind::SystemActor, "Operator console", None); add(&mut p, 11, Kind::System, "Valve service", None); for (port, owner) in [(12, 10), (13, 11)] { add(&mut p, port, Kind::Port, "Service socket", Some(id(owner))); body( &mut p, port, SharedBody::Port { contract: PortContract::Component { kind: ComponentPortKind::Standard, orientation: ComponentPortOrientation::Unset, }, legacy: None, }, ); } for (operation, name) in [(20, "Reset"), (21, "ReadStatus")] { add(&mut p, operation, Kind::ExchangeItem, name, None); body( &mut p, operation, SharedBody::ExchangeItem { kind: ExchangeItemKind::Information, mechanism: ExchangeMechanism::Operation, supertype: None, }, ); } add(&mut p, 22, Kind::Interface, "Valve commands", None); for (allocation, item) in [(30, 20), (31, 21)] { rel( &mut p, allocation, RelationKind::InterfaceItemAllocation, 22, item, ); let RelationBody::InterfaceItemAllocation { transmission, .. } = &mut p.relations.get_mut(&id(allocation)).unwrap().body else { panic!() }; *transmission = TransmissionProtocol::Synchronous; } rel(&mut p, 32, RelationKind::InterfaceRequirement, 12, 22); rel(&mut p, 33, RelationKind::InterfaceProvision, 13, 22); rel(&mut p, 34, RelationKind::BoundaryExchange, 12, 13); p.relations.get_mut(&id(34)).unwrap().body = RelationBody::BoundaryExchange { source: id(12), target: id(13), oriented: false, items: vec![id(20), id(21)], }; add(&mut p, 40, Kind::Scenario, "Reset then read status", None); body( &mut p, 40, SharedBody::Scenario { viewpoint: ScenarioViewpoint::BoundaryInterface, context: None, precondition: String::new(), postcondition: String::new(), steps: vec![id(43), id(44)], }, ); for (line, participant) in [(41, 10), (42, 11)] { add( &mut p, line, Kind::Lifeline, "Service participant", Some(id(40)), ); body( &mut p, line, SharedBody::Lifeline { represented: Some(id(participant)), }, ); } for (call, name) in [(43, "Call Reset"), (44, "Call ReadStatus")] { add(&mut p, call, Kind::Occurrence, name, Some(id(40))); body( &mut p, call, SharedBody::Occurrence { body: OccurrenceBody::Exchange { exchange: Some(id(34)), sender: Some(id(41)), receiver: Some(id(42)), message_kind: MessageKind::Call, reply_to: None, values: vec![], }, precondition: String::new(), postcondition: String::new(), }, ); } valid(&p); p } fn refined(p: &Project) -> Project { let mut q = p.clone(); for (work, owner, name) in [ (50, 10, "Issue service request"), (51, 11, "Execute service request"), ] { add(&mut q, work, Kind::Function, name, None); rel(&mut q, work + 10, RelationKind::Allocation, owner, work); } for (item, output, input, exchange) in [(20, 70, 71, 80), (21, 72, 73, 81)] { add(&mut q, output, Kind::Port, "Operation output", Some(id(50))); add(&mut q, input, Kind::Port, "Operation input", Some(id(51))); body( &mut q, output, SharedBody::Port { contract: PortContract::FunctionOutput { outgoing_items: vec![id(item)], }, legacy: None, }, ); body( &mut q, input, SharedBody::Port { contract: PortContract::FunctionInput { incoming_items: vec![id(item)], }, legacy: None, }, ); rel(&mut q, exchange, RelationKind::FunctionalExchange, 50, 51); q.relations.get_mut(&id(exchange)).unwrap().body = RelationBody::FunctionalExchange { source: id(50), target: id(51), source_port: id(output), target_port: id(input), items: vec![id(item)], }; rel( &mut q, output + 20, RelationKind::PortAllocation, 12, output, ); rel(&mut q, input + 20, RelationKind::PortAllocation, 13, input); rel( &mut q, exchange + 20, RelationKind::ExchangeAllocation, 34, exchange, ); } set_exchange(&mut q, 43, id(80)); set_exchange(&mut q, 44, id(81)); valid(&q); q } fn write(p: &Project, out: &Path, name: &str) { let source = language::format(p); let bundle = source_project::SourceBundle { entry: "entry.arcadia".into(), files: BTreeMap::from([("entry.arcadia".into(), source.clone())]), }; assert_eq!( &source_project::compile_bundle(&bundle, Default::default()) .unwrap() .decoded .project, p ); assert_eq!( &serde_json::from_value::(serde_json::to_value(p).unwrap()).unwrap(), p ); fs::write(out.join(format!("{name}.arcadia")), source).unwrap(); fs::write( out.join(format!("{name}.json")), serde_json::to_vec_pretty(p).unwrap(), ) .unwrap(); } fn main() { let directory = std::env::args().nth(1).expect("probe OUTPUT_DIRECTORY"); let out = Path::new(&directory); fs::create_dir_all(out).unwrap(); let p = initial(); write(&p, out, "boundary-only"); assert_eq!( message(&p, 43), message(&p, 44), "Only prose distinguishes the intended operations" ); assert!(!references::element_references(&p.elements[&id(43)]).contains(&id(20))); assert!(!references::element_references(&p.elements[&id(44)]).contains(&id(21))); for target in [20, 30] { let mut invalid = p.clone(); set_exchange(&mut invalid, 43, id(target)); assert!( validate(&invalid) .iter() .any(|i| i.code == "occurrence_exchange" && i.element == Some(id(43))) ); } let mut proposed = serde_json::to_value(message(&p, 43)).unwrap(); proposed["Exchange"]["operation"] = serde_json::to_value(id(30)).unwrap(); assert!( serde_json::from_value::(proposed) .unwrap_err() .to_string() .contains("unknown field") ); let mut session = Session::new(p.clone()); let mut reset = p.elements[&id(20)].clone(); reset.name = "EmergencyReset".into(); session .execute(Command::EditElement(id(20), reset)) .unwrap(); assert_eq!(message(session.project(), 43), message(&p, 43)); session.execute(Command::Delete(id(20))).unwrap(); assert_eq!(message(session.project(), 43), message(&p, 43)); assert!(session.project().elements.contains_key(&id(43))); valid(session.project()); write(session.project(), out, "after-reset-deletion"); let q = refined(&p); write(&q, out, "explicit-functional-refinement"); assert_eq!(q.relations[&id(34)], p.relations[&id(34)]); assert_ne!(message(&q, 43), message(&q, 44)); for n in [43, 44] { assert!( references::element_references(&q.elements[&id(n)]) .iter() .any(|v| [id(80), id(81)].contains(v)) ); } println!( "PASS validated boundary-only model: {} elements / {} relations, two parameterless Operations", p.elements.len(), p.relations.len() ); println!( "PASS identical typed Calls; item/allocation exchange substitutions rejected; operation DTO field rejected" ); println!( "PASS operation rename/delete leaves intended Call's typed body unchanged; Call remains structurally valid" ); println!( "PASS distinct FE Calls require this explicit refinement: +{} elements / +{} relations; same original boundary", q.elements.len() - p.elements.len(), q.relations.len() - p.relations.len() ); println!("PASS all three complete source/JSON round trips; no production mutation"); }