1use crate::{
7 CloudEvent, Digest, Error, InvocationIdentity, ProgramDescriptor, ProgramOutcome, ProgramRef,
8};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct ExecutionRequest {
14 pub descriptor: ProgramDescriptor,
15 pub event: CloudEvent,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ExecutionContext {
20 #[serde(flatten)]
21 pub identity: InvocationIdentity,
22 pub program: ProgramRef,
23 pub digest: Digest,
24}
25impl From<&ExecutionRequest> for ExecutionContext {
26 fn from(request: &ExecutionRequest) -> Self {
27 Self {
28 identity: InvocationIdentity::from(&request.event),
29 program: request.descriptor.program.clone(),
30 digest: request.descriptor.digest.clone(),
31 }
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ExecutionReport {
37 #[serde(flatten)]
38 pub context: Box<ExecutionContext>,
39 pub process_id: u32,
40 pub reused_process: bool,
41 pub outcome: ProgramOutcome,
42 pub elapsed_ms: u64,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case")]
47pub enum Phase {
48 Admission,
49 Preparation,
50 Startup,
51 Execution,
52 Cleanup,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ExecutionFailure {
57 #[serde(flatten)]
58 pub context: Box<ExecutionContext>,
59 pub error: Error,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub cleanup_error: Option<Error>,
62 pub phase: Phase,
63 pub execution_may_have_started: bool,
65}
66impl std::fmt::Display for ExecutionFailure {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(f, "{:?}: {}", self.phase, self.error)
69 }
70}
71impl std::error::Error for ExecutionFailure {}
72pub type ExecutionResult = std::result::Result<ExecutionReport, ExecutionFailure>;
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(deny_unknown_fields)]
78pub struct RuntimeInvocation {
79 pub event: CloudEvent,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub processing_context: Option<crate::TraceContext>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub extension: Option<RuntimeExtension>,
85}
86impl From<CloudEvent> for RuntimeInvocation {
87 fn from(event: CloudEvent) -> Self {
88 Self {
89 event,
90 processing_context: None,
91 extension: None,
92 }
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98#[serde(deny_unknown_fields)]
99pub struct RuntimeExtension {
100 pub schema: String,
101 pub payload: Value,
102}
103impl RuntimeExtension {
104 pub fn validate(&self) -> crate::Result<()> {
105 validate_name(&self.schema, "runtime extension schema")?;
106 crate::validate_runtime_payload(&self.payload, crate::RUNTIME_EXTENSION_MAX_BYTES)
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112#[serde(deny_unknown_fields)]
113pub struct RuntimeRequest {
114 pub id: u64,
115 pub operation: String,
116 pub payload: Value,
117}
118impl RuntimeRequest {
119 pub fn validate(&self) -> crate::Result<()> {
120 if self.id == 0 {
121 return Err(Error::new(
122 crate::ErrorKind::InvalidInput,
123 "runtime request ID must be positive",
124 ));
125 }
126 validate_name(&self.operation, "runtime request operation")?;
127 crate::validate_runtime_payload(&self.payload, crate::RUNTIME_REQUEST_MAX_BYTES)
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
133#[serde(deny_unknown_fields)]
134pub struct RuntimeReply {
135 pub id: u64,
136 pub result: Value,
137}
138
139fn validate_name(value: &str, label: &str) -> crate::Result<()> {
140 if value.is_empty() || value.len() > 256 || value.chars().any(char::is_control) {
141 return Err(Error::new(
142 crate::ErrorKind::InvalidInput,
143 format!("{label} must contain 1..=256 bytes without control characters"),
144 ));
145 }
146 Ok(())
147}