flow_component/
context.rs

1use std::sync::Arc;
2
3use seeded_random::{Random, Seed};
4use serde::de::DeserializeOwned;
5use serde::Serialize;
6use wick_packet::{date_from_millis, ContextTransport, DateTime, InherentData};
7
8#[cfg(target_family = "wasm")]
9/// A conditional trait that implements Send if the target is not wasm.
10pub trait LocalAwareSend {}
11#[cfg(not(target_family = "wasm"))]
12/// A conditional trait that implements Send if the target is not wasm.
13pub trait LocalAwareSend: Send {}
14
15#[cfg(target_family = "wasm")]
16impl<T> LocalAwareSend for T {}
17
18#[cfg(not(target_family = "wasm"))]
19impl<T> LocalAwareSend for T where T: Send {}
20
21#[derive(Clone)]
22#[non_exhaustive]
23/// A context that is passed to a component's operations.
24pub struct Context<T>
25where
26  T: std::fmt::Debug,
27  T: LocalAwareSend,
28{
29  /// Operation-specific configuration.
30  pub config: Arc<T>,
31  /// Inherent data passed to the operation.
32  pub inherent: InherentContext,
33  #[cfg(feature = "invocation")]
34  /// A callback to invoke other components within the executing runtime.
35  pub callback: LocalScope,
36}
37
38impl<T> std::fmt::Debug for Context<T>
39where
40  T: std::fmt::Debug + DeserializeOwned + Serialize,
41  T: LocalAwareSend,
42{
43  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44    f.debug_struct("Context").field("config", &self.config).finish()
45  }
46}
47
48impl<T> From<ContextTransport<T>> for Context<T>
49where
50  T: std::fmt::Debug + Serialize + DeserializeOwned,
51  T: LocalAwareSend,
52{
53  fn from(value: ContextTransport<T>) -> Self {
54    Self {
55      inherent: InherentContext {
56        rng: Random::from_seed(Seed::unsafe_new(value.inherent.seed)),
57        timestamp: date_from_millis(value.inherent.timestamp).unwrap(),
58      },
59      config: Arc::new(value.config),
60      #[cfg(feature = "invocation")]
61      callback: Default::default(),
62    }
63  }
64}
65
66#[derive(Debug)]
67#[non_exhaustive]
68/// Inherent data passed to an operation.
69pub struct InherentContext {
70  /// A random number generator initialized from the invocation seed.
71  pub rng: Random,
72  /// The timestamp of the invocation.
73  pub timestamp: DateTime,
74}
75
76impl Clone for InherentContext {
77  fn clone(&self) -> Self {
78    Self {
79      rng: Random::from_seed(self.rng.seed()),
80      timestamp: self.timestamp,
81    }
82  }
83}
84
85impl From<InherentContext> for InherentData {
86  fn from(value: InherentContext) -> Self {
87    Self::new(value.rng.gen(), value.timestamp.timestamp_millis() as _)
88  }
89}
90
91impl From<InherentData> for InherentContext {
92  fn from(value: InherentData) -> Self {
93    Self {
94      rng: Random::from_seed(Seed::unsafe_new(value.seed)),
95      timestamp: date_from_millis(value.timestamp).unwrap(),
96    }
97  }
98}
99
100impl<T> Context<T>
101where
102  T: std::fmt::Debug,
103  T: LocalAwareSend,
104{
105  /// Create a new context.
106  #[cfg(feature = "invocation")]
107  pub fn new(config: T, inherent: &InherentData, callback: LocalScope) -> Self {
108    Self {
109      inherent: InherentContext {
110        rng: Random::from_seed(Seed::unsafe_new(inherent.seed)),
111        timestamp: date_from_millis(inherent.timestamp).unwrap(),
112      },
113      config: Arc::new(config),
114      callback,
115    }
116  }
117
118  /// Create a new context.
119  #[cfg(not(feature = "invocation"))]
120  pub fn new(config: T, inherent: &InherentData) -> Self {
121    Self {
122      inherent: InherentContext {
123        rng: Random::from_seed(Seed::unsafe_new(inherent.seed)),
124        timestamp: date_from_millis(inherent.timestamp).unwrap(),
125      },
126      config: Arc::new(config),
127    }
128  }
129}
130
131#[allow(missing_debug_implementations, missing_copy_implementations)]
132#[derive(Default, Clone)]
133#[non_exhaustive]
134/// The [LocalScope] type is used to invoke other components within the executing scope.
135pub struct LocalScope {
136  #[allow(unused)]
137  #[cfg(feature = "invocation")]
138  invocation: Option<Arc<crate::ScopeInvokeFn>>,
139}
140
141impl LocalScope {
142  /// Initialize a new environment context.
143  #[must_use]
144  #[cfg(feature = "invocation")]
145  pub fn new(invocation: Arc<crate::ScopeInvokeFn>) -> Self {
146    Self {
147      invocation: Some(invocation),
148    }
149  }
150  #[cfg(feature = "invocation")]
151  #[must_use]
152  /// Invoke a component on a foreign runtime.
153  pub fn invoke(
154    &self,
155    component_ref: wick_packet::ComponentReference,
156    op: String,
157    stream: wick_packet::PacketStream,
158    inherent: InherentData,
159    config: Option<wick_packet::RuntimeConfig>,
160    span: &tracing::Span,
161  ) -> crate::BoxFuture<'static, Result<wick_packet::PacketStream, crate::ComponentError>> {
162    self.invocation.as_ref().map_or_else(
163      || {
164        panic!("invocation not configured");
165      },
166      |invoke| (invoke)(component_ref, op, stream, inherent, config, span),
167    )
168  }
169  #[must_use]
170  #[allow(clippy::missing_const_for_fn)]
171  /// Initialize a new environment context.
172  #[cfg(not(feature = "invocation"))]
173  pub fn new() -> Self {
174    Self {}
175  }
176}