Skip to main content

rustev_surface/
run.rs

1//! The result envelope of `run` (spec 006, 3.5): how a decision's outcome
2//! is printed, whichever host ran it. Running the decision is the host's
3//! (the runtime is not a dependency of this crate); the host maps its
4//! runtime's results onto these.
5
6use rustev_contract::Document;
7use rustev_contract::ids::PlanId;
8use rustev_contract::judgment::Judgment;
9use rustev_contract::replay::CaptureStatus;
10use rustev_contract::run::{RunRecord, Termination};
11use serde_json::json;
12
13use crate::out::{Done, Out, code};
14
15/// The fields every decided or rejected `run` prints.
16pub fn base(command: &str, decision_id: &str, plan_id: &PlanId) -> Out {
17    Out::new(command, "")
18        .set("decision_id", decision_id)
19        .set("plan_id", plan_id)
20}
21
22/// A decision rejected before admission: its kind and detail.
23pub fn rejected(base: Out, kind: &str, detail: &str) -> Done {
24    Done::new(
25        code::REJECTED,
26        base.set("status", "rejected")
27            .set("rejection", kind)
28            .set("detail", detail),
29    )
30}
31
32/// A delivered decision: where its record went and the sink's receipt.
33pub fn delivered(base: Out, record: &str, receipt: &str) -> Out {
34    base.set("record", record).set("receipt", receipt)
35}
36
37/// A judged decision (`out` from [`delivered`]).
38pub fn judged(out: Out, j: &Judgment) -> (Out, u8) {
39    (
40        addressing(
41            out.set("status", "judged")
42                .set("completion", "judged")
43                .raw("judgment", judgment_bytes(j)),
44            j,
45        ),
46        code::OK,
47    )
48}
49
50/// A cancelled decision (`out` from [`delivered`]).
51pub fn cancelled(out: Out) -> (Out, u8) {
52    (
53        out.set("status", "cancelled")
54            .set("completion", "cancelled"),
55        code::CANCELLED,
56    )
57}
58
59/// A decision whose evidence was not delivered: the record is evidence, not
60/// a result to act on.
61pub fn not_delivered(base: Out, record: &RunRecord, detail: &str, uncertain: bool) -> (Out, u8) {
62    let completion = match record.termination {
63        Termination::Judged => "judged",
64        Termination::Cancelled => "cancelled",
65    };
66    (
67        base.set("status", "evidence_not_delivered")
68            .set("completion", completion)
69            .set("detail", detail)
70            .set("uncertain", &uncertain)
71            .raw("run_record", record_bytes(record)),
72        code::EVIDENCE_NOT_DELIVERED,
73    )
74}
75
76/// For a `rustev.judgment/2`, the outcome's `addressed_to` and `transient`
77/// beside the judgment (spec 033, 3.11.3): an unresolved or `needs_input`
78/// outcome's action party and transience, a refusal's combined party and
79/// `false`, and `null` for a proposal or an escalation (`escalate`, which
80/// carries no action). A version 1 judgment's output keeps its bytes. The
81/// status stays `judged` and the exit code 0 for every outcome.
82fn addressing(out: Out, j: &Judgment) -> Out {
83    use rustev_contract::judgment::Outcome;
84    if j.schema != rustev_contract::schema::JUDGMENT_V2 {
85        return out;
86    }
87    let (to, transient) = match &j.outcome {
88        Outcome::UnresolvedAction { action, .. } => {
89            (json!(action.addressed_to), json!(action.transient))
90        }
91        Outcome::NeedsInput(n) => (json!(n.action.addressed_to), json!(n.action.transient)),
92        Outcome::Refused { addressed_to, .. } => (json!(addressed_to), json!(false)),
93        _ => (json!(null), json!(null)),
94    };
95    out.set("addressed_to", &to).set("transient", &transient)
96}
97
98/// A judgment's record canonical bytes.
99pub fn judgment_bytes(j: &Judgment) -> Vec<u8> {
100    // Every judgment the core finishes has a record form; a failure here is
101    // a defect in the core's number handling.
102    j.record_canonical()
103        .expect("a judgment has a record canonical form")
104}
105
106/// A run record's record canonical bytes.
107pub fn record_bytes(r: &RunRecord) -> Vec<u8> {
108    r.record_canonical()
109        .expect("a run record has a record canonical form")
110}
111
112/// The name of a capture's status.
113pub fn capture_status(s: CaptureStatus) -> &'static str {
114    match s {
115        CaptureStatus::Complete => "complete",
116        CaptureStatus::Disabled => "disabled",
117        CaptureStatus::LimitExceeded => "limit_exceeded",
118        CaptureStatus::Cancelled => "cancelled",
119        CaptureStatus::Abandoned => "abandoned",
120    }
121}