Skip to main content

a3s_code_core/capability/
flow_binding.rs

1use std::fmt;
2
3use a3s_flow::{FlowEngine, FlowError, WorkflowSpec};
4
5/// Immutable A3S Flow definition paired with the exact engine that can replay it.
6///
7/// [`WorkflowSpec::name`] is the public capability name and remains the single
8/// source of truth for lookup and descriptor validation. The engine retains its
9/// own event store, runtime, observer, and runtime-build compatibility policy;
10/// Code does not duplicate those A3S Flow lifecycles.
11#[derive(Clone)]
12pub struct FlowBinding {
13    spec: WorkflowSpec,
14    engine: FlowEngine,
15}
16
17impl FlowBinding {
18    /// Validate and bind one immutable workflow definition to its executor.
19    pub fn new(spec: WorkflowSpec, engine: FlowEngine) -> a3s_flow::Result<Self> {
20        spec.validate()?;
21        if !engine.supports_runtime_build(spec.runtime_build_id.as_ref()) {
22            let required = spec
23                .runtime_build_id
24                .as_ref()
25                .map(ToString::to_string)
26                .unwrap_or_else(|| "<unpinned>".to_owned());
27            let current = engine
28                .runtime_build_compatibility()
29                .map(|compatibility| compatibility.current_build_id().to_string())
30                .unwrap_or_else(|| "<unfenced>".to_owned());
31            return Err(FlowError::InvalidWorkflow(format!(
32                "workflow '{}' requires runtime build {required}, but its engine exposes {current}",
33                spec.name
34            )));
35        }
36        Ok(Self { spec, engine })
37    }
38
39    /// Stable public name used by the Code capability descriptor and host lookup.
40    pub fn public_name(&self) -> &str {
41        &self.spec.name
42    }
43
44    /// Exact durable definition used for every run started through this binding.
45    pub fn spec(&self) -> &WorkflowSpec {
46        &self.spec
47    }
48
49    pub(crate) fn engine(&self) -> &FlowEngine {
50        &self.engine
51    }
52}
53
54impl fmt::Debug for FlowBinding {
55    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
56        formatter
57            .debug_struct("FlowBinding")
58            .field("public_name", &self.public_name())
59            .field("version", &self.spec.version)
60            .field("runtime", &self.spec.runtime)
61            .field("runtime_build_id", &self.spec.runtime_build_id)
62            .finish_non_exhaustive()
63    }
64}