Files
ARCADIA-Studio/harness/fixtures/release1-native/artifacts/generate-semantic-artifacts.rs
T

264 lines
9.1 KiB
Rust

use oa_core::{transitions::*, *};
use std::{collections::BTreeMap, fs, path::Path};
const META: [ArtifactFacet; 7] = [
ArtifactFacet::Name,
ArtifactFacet::Description,
ArtifactFacet::Owner,
ArtifactFacet::Relation,
ArtifactFacet::Uri,
ArtifactFacet::Revision,
ArtifactFacet::Fragment,
];
fn request(
p: &Project,
trace: &Trace,
keys: KeyPair<Id>,
decisions: Vec<(ArtifactFacet, SemanticDecision)>,
candidate: Option<ArtifactLink>,
) -> SemanticTransitionRequest {
let mut t = TransitionRequest::new(TransitionDirection::OaToSa, vec![trace.oa]);
t.mappings.push(TransitionMapping {
source: trace.oa,
target: Some(trace.sa),
target_kind: None,
});
let mut r = SemanticTransitionRequest::new(t);
r.traces.insert(trace.oa, trace.id);
r.references.insert(trace.oa, trace.sa);
if let Some((oa, sa)) = keys.oa.zip(keys.sa) {
r.references.insert(oa, sa);
}
r.reviews.push(SemanticReviewRequest {
source: trace.oa,
resolutions: decisions
.into_iter()
.map(|(facet, decision)| FacetResolution {
facet: FacetSelector::Artifact {
keys: keys.clone(),
facet,
},
decision,
})
.collect(),
reset: vec![],
});
if let Some(c) = candidate {
r.candidates.push(SemanticRecord::Artifact(c));
}
validate_candidate(p).unwrap();
r
}
fn emit(dir: &Path, p: &Project, r: SemanticTransitionRequest, name: &str, preserve_base: bool) {
let plan = plan_semantic_transition(p, r.clone()).unwrap();
assert!(plan.conflicts.is_empty(), "{:?}", plan.conflicts);
let after = prepare_semantic_transition(p, &plan).unwrap().project;
let logical = format!("{name}.arcadia");
let source = if preserve_base {
fs::read_to_string(dir.join(&logical)).expect("Read unchanged fixture source")
} else {
format!(
"// Exact artifact rationale: no implicit metadata review\r\n{}",
language::format(p)
)
};
let bundle = source_project::SourceBundle {
entry: logical.clone(),
files: BTreeMap::from([(logical.clone(), source.clone())]),
};
let compiled =
source_project::compile_bundle(&bundle, source_project::ProjectLimits::default()).unwrap();
let updated =
source_project::update_bundle(&compiled, &after, source_project::ProjectLimits::default())
.unwrap();
for (suffix, project, text) in [
("", p, &source),
(".after", &after, &updated.bundle.files[&logical]),
] {
assert_eq!(language::parse(text).unwrap(), *project);
if preserve_base && suffix.is_empty() {
continue;
}
fs::write(dir.join(format!("{name}{suffix}.arcadia")), text).unwrap();
fs::write(
dir.join(format!("{name}{suffix}.json")),
serde_json::to_vec_pretty(project).unwrap(),
)
.unwrap();
}
fs::write(
dir.join(format!("{name}.request.json")),
serde_json::to_vec_pretty(&r).unwrap(),
)
.unwrap();
let manifest = serde_json::json!({
"fixture":name,"schema":p.schema_version,"project":p.id,
"trace_ids":r.traces.values().collect::<Vec<_>>(),
"candidate_ids":r.candidates.iter().map(SemanticRecord::id).collect::<Vec<_>>(),
"initial_artifact_ids":p.artifact_links.keys().collect::<Vec<_>>(),
"final_artifact_ids":after.artifact_links.keys().collect::<Vec<_>>(),
"acknowledged_count":plan.reviews.iter().map(|review|review.acknowledged.len()).sum::<usize>(),
"retired_count":plan.reviews.iter().map(|review|review.retired.len()).sum::<usize>(),
"impact_count":plan.impacts.len(),"native_acceptance":"pending"
});
fs::write(
dir.join(format!("{name}.manifest.json")),
serde_json::to_vec_pretty(&manifest).unwrap(),
)
.unwrap();
println!(
"{name}: {} artifacts -> {}; {} reviewed facts, {} retired, {} impacts",
p.artifact_links.len(),
after.artifact_links.len(),
plan.reviews[0].acknowledged.len(),
plan.reviews[0].retired.len(),
plan.impacts.len()
);
}
fn main() {
let args: Vec<_> = std::env::args().collect();
assert!(
(2..=3).contains(&args.len()),
"Usage: generate-semantic-artifacts OUTPUT_DIRECTORY [RESERVED_CREATION_UUID]. Without UUID, creates fresh fixture identities; with UUID, preserves the existing creation base and writes its exact after oracle."
);
let dir = Path::new(&args[1]);
fs::create_dir_all(dir).unwrap();
if args.len() == 3 {
let p: Project =
serde_json::from_slice(&fs::read(dir.join("semantic-artifacts-create.json")).unwrap())
.unwrap();
let trace = p
.traces
.values()
.find(|t| t.alias == "artifact_review")
.unwrap();
let source = p
.artifact_links
.values()
.find(|a| a.owner == trace.oa)
.unwrap();
let mut candidate = source.clone();
candidate.id = args[2].parse().unwrap();
candidate.alias = format!("artifact_{}", candidate.id.simple());
candidate.owner = trace.sa;
let keys = KeyPair {
oa: Some(source.id),
sa: Some(candidate.id),
};
let mut decisions = vec![(ArtifactFacet::Presence, SemanticDecision::TakeSource)];
decisions.extend(META.map(|facet| (facet, SemanticDecision::Candidate)));
emit(
dir,
&p,
request(&p, trace, keys, decisions, Some(candidate)),
"semantic-artifacts-create",
true,
);
return;
}
let mut p = Project::empty();
p.name = "Explicit requirement-link review".into();
let mut owners = vec![];
let mut others = vec![];
let mut links = vec![];
for layer in [Layer::Oa, Layer::Sa] {
let prefix = if layer == Layer::Oa { "OA" } else { "SA" };
let kind = if layer == Layer::Oa {
Kind::Activity
} else {
Kind::Function
};
let owner = p
.add_element(kind, layer, &format!("{prefix} Startup coordination"), None)
.unwrap();
let other = p
.add_element(kind, layer, &format!("{prefix} Safety coordination"), None)
.unwrap();
let id = Id::new_v4();
p.artifact_links.insert(
id,
ArtifactLink {
id,
alias: format!("artifact_{}", id.simple()),
name: format!("{prefix} Interlock requirement"),
description: "Reviewed safety rationale\r\nSecond exact line".into(),
owner,
relation: ArtifactRelation::Satisfies,
uri: "requirements/interlock".into(),
revision: None,
fragment: Some("section-4".into()),
},
);
owners.push(owner);
others.push(other);
links.push(id);
}
let id = Id::new_v4();
let trace = Trace {
id,
alias: "artifact_review".into(),
name: "Interlock requirement rationale".into(),
description: String::new(),
oa: owners[0],
sa: owners[1],
kind: TraceKind::Manual,
baseline: None,
};
p.traces.insert(id, trace.clone());
let keys = KeyPair {
oa: Some(links[0]),
sa: Some(links[1]),
};
let mut moved = p.artifact_links[&links[1]].clone();
moved.owner = others[1];
moved.revision = Some(String::new());
moved.fragment = None;
emit(
dir,
&p,
request(
&p,
&trace,
keys.clone(),
vec![
(ArtifactFacet::Owner, SemanticDecision::Candidate),
(ArtifactFacet::Revision, SemanticDecision::Candidate),
(ArtifactFacet::Fragment, SemanticDecision::Candidate),
],
Some(moved),
),
"semantic-artifacts-move",
false,
);
let mut deleted = p.clone();
deleted.artifact_links.get_mut(&links[0]).unwrap().owner = others[0];
emit(
dir,
&deleted,
request(
&deleted,
&trace,
keys.clone(),
vec![(ArtifactFacet::Presence, SemanticDecision::TakeSource)],
None,
),
"semantic-artifacts-delete",
false,
);
let mut create = p.clone();
create.artifact_links.remove(&links[1]);
let source = create.artifact_links[&links[0]].clone();
let mut candidate = source;
candidate.id = links[1];
candidate.alias = format!("artifact_{}", candidate.id.simple());
candidate.owner = owners[1];
let mut decisions = vec![(ArtifactFacet::Presence, SemanticDecision::TakeSource)];
decisions.extend(META.map(|facet| (facet, SemanticDecision::Candidate)));
emit(
dir,
&create,
request(&create, &trace, keys, decisions, Some(candidate)),
"semantic-artifacts-create",
false,
);
}