Integrate release recovery, semantic review, coverage and Windows packaging
This commit is contained in:
@@ -10,7 +10,7 @@ use iced::{
|
||||
use oa_core::*;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Action {
|
||||
Component,
|
||||
Input,
|
||||
@@ -83,7 +83,7 @@ pub fn actions(p: &Project, id: Id) -> Vec<Action> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ContractKind {
|
||||
Input,
|
||||
Output,
|
||||
@@ -128,7 +128,104 @@ pub struct State {
|
||||
primary_input: bool,
|
||||
preview: Result<Vec<Command>, String>,
|
||||
}
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct RecoveryState {
|
||||
version: u32,
|
||||
base: String,
|
||||
action: Action,
|
||||
subject: Id,
|
||||
id: Id,
|
||||
name: String,
|
||||
kind: ContractKind,
|
||||
orientation: ComponentPortOrientation,
|
||||
oriented: bool,
|
||||
boundary: Option<Id>,
|
||||
source_port: PortChoice,
|
||||
target_port: PortChoice,
|
||||
incoming: BTreeSet<Id>,
|
||||
outgoing: BTreeSet<Id>,
|
||||
primary_input: bool,
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod recovery_tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn port_recovery_retains_authored_contract_and_identity_but_rebuilds_preview() {
|
||||
let mut p = Project::empty();
|
||||
let owner = p
|
||||
.add_element(Kind::Function, Layer::Sa, "Receiver", None)
|
||||
.unwrap();
|
||||
let item = p
|
||||
.add_element(Kind::ExchangeItem, Layer::Sa, "Payload", None)
|
||||
.unwrap();
|
||||
let mut state = State::new(&p, owner, Action::Input).unwrap();
|
||||
state.name = " exact pending port ".into();
|
||||
state.incoming.insert(item);
|
||||
state.primary_input = false;
|
||||
state.preview = Err("An old transient preview must not be restored".into());
|
||||
let saved = state.recovery_snapshot().unwrap();
|
||||
assert!(saved.get("preview").is_none());
|
||||
let restored = State::from_recovery(&p, saved.clone()).unwrap();
|
||||
assert_eq!(restored.recovery_snapshot().unwrap(), saved);
|
||||
assert_eq!(restored.id, state.id);
|
||||
assert!(restored.preview.is_ok());
|
||||
p.name.push('!');
|
||||
assert!(State::from_recovery(&p, saved).is_err());
|
||||
}
|
||||
}
|
||||
impl State {
|
||||
pub fn recovery_snapshot(&self) -> Result<serde_json::Value, String> {
|
||||
serde_json::to_value(RecoveryState {
|
||||
version: 1,
|
||||
base: transitions::project_fingerprint(&self.baseline),
|
||||
action: self.action,
|
||||
subject: self.subject,
|
||||
id: self.id,
|
||||
name: self.name.clone(),
|
||||
kind: self.kind,
|
||||
orientation: self.orientation,
|
||||
oriented: self.oriented,
|
||||
boundary: self.boundary.as_ref().map(|c| c.id),
|
||||
source_port: self.source_port.choice,
|
||||
target_port: self.target_port.choice,
|
||||
incoming: self.incoming.clone(),
|
||||
outgoing: self.outgoing.clone(),
|
||||
primary_input: self.primary_input,
|
||||
})
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
pub fn from_recovery(p: &Project, value: serde_json::Value) -> Result<Self, String> {
|
||||
let saved: RecoveryState = serde_json::from_value(value).map_err(|e| e.to_string())?;
|
||||
if saved.version != 1 || saved.base != transitions::project_fingerprint(p) {
|
||||
return Err(
|
||||
"Port workflow recovery does not match the accepted model identity and revision."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
if saved.incoming.len().saturating_add(saved.outgoing.len())
|
||||
> crate::source_workspace::LIMITS.model.max_records
|
||||
{
|
||||
return Err("Recovered port draft exceeds item limits.".into());
|
||||
}
|
||||
let mut state = Self::new(p, saved.subject, saved.action)?;
|
||||
state.id = saved.id;
|
||||
state.name = saved.name;
|
||||
state.kind = saved.kind;
|
||||
state.orientation = saved.orientation;
|
||||
state.oriented = saved.oriented;
|
||||
state.boundary = saved.boundary.map(|id| Choice {
|
||||
id,
|
||||
label: p.named(id).into(),
|
||||
});
|
||||
state.source_port = workflow::recovery::endpoint(p, saved.source_port);
|
||||
state.target_port = workflow::recovery::endpoint(p, saved.target_port);
|
||||
state.incoming = saved.incoming;
|
||||
state.outgoing = saved.outgoing;
|
||||
state.primary_input = saved.primary_input;
|
||||
state.preview = state.plan(p);
|
||||
Ok(state)
|
||||
}
|
||||
pub fn new(p: &Project, subject: Id, action: Action) -> Result<Self, String> {
|
||||
if !actions(p, subject).contains(&action) {
|
||||
return Err("This port action is unavailable for the selected element.".into());
|
||||
|
||||
Reference in New Issue
Block a user