//// Typed activity values and policy configuration. import aion/codec import aion/duration import aion/error import gleam/option.{type Option, None, Some} /// Backoff strategy carried with an explicit retry policy. /// /// The SDK only stores this configuration for the engine to interpret during /// dispatch. It does not apply retries or invent missing timing values. pub type Backoff { /// Exponential backoff from `initial`, scaled by `multiplier`, capped at `max`. Exponential( initial: duration.Duration, multiplier: Float, max: duration.Duration, ) /// Linear backoff from `initial`, adding `increment`, capped at `max`. Linear( initial: duration.Duration, increment: duration.Duration, max: duration.Duration, ) /// Fixed backoff with the same `delay` between attempts. Fixed(delay: duration.Duration) } /// Explicit retry policy for an activity. /// /// No default retry policy is baked into the SDK. An activity built with `new` /// and no `retry` decorator carries no policy and runs exactly once when the /// engine dispatches it. pub type RetryPolicy { RetryPolicy(max_attempts: Int, backoff: Backoff) } /// A typed activity invocation value. /// /// `i` is the statically-known input type and `o` is the statically-known output /// type. The input and output codecs are carried so workflow dispatch can encode /// the author value and decode the type-erased engine payload without /// reflection. pub opaque type Activity(i, o) { Activity( name: String, input: i, input_codec: codec.Codec(i), output_codec: codec.Codec(o), runner: fn(i) -> Result(o, error.ActivityError), retry_policy: Option(RetryPolicy), timeout: Option(duration.Duration), heartbeat: Option(duration.Duration), ) } /// Build a typed activity value with no retry, timeout, or heartbeat config. /// /// Absence of config is intentional data: there are no hidden defaults. In /// particular, an activity with no `retry` decorator runs exactly once when it /// is dispatched by the engine. pub fn new( name: String, input: i, input_codec: codec.Codec(i), output_codec: codec.Codec(o), run: fn(i) -> Result(o, error.ActivityError), ) -> Activity(i, o) { Activity( name: name, input: input, input_codec: input_codec, output_codec: output_codec, runner: run, retry_policy: None, timeout: None, heartbeat: None, ) } /// Attach an explicit retry policy to an activity. /// /// Later calls replace earlier retry policy values; the SDK does not merge or /// synthesize policy fields. pub fn retry(activity: Activity(i, o), policy: RetryPolicy) -> Activity(i, o) { Activity( name: activity.name, input: activity.input, input_codec: activity.input_codec, output_codec: activity.output_codec, runner: activity.runner, retry_policy: Some(policy), timeout: activity.timeout, heartbeat: activity.heartbeat, ) } /// Attach an explicit timeout duration to an activity. pub fn timeout( activity: Activity(i, o), timeout_duration: duration.Duration, ) -> Activity(i, o) { Activity( name: activity.name, input: activity.input, input_codec: activity.input_codec, output_codec: activity.output_codec, runner: activity.runner, retry_policy: activity.retry_policy, timeout: Some(timeout_duration), heartbeat: activity.heartbeat, ) } /// Attach an explicit heartbeat interval to an activity. pub fn heartbeat( activity: Activity(i, o), heartbeat_interval: duration.Duration, ) -> Activity(i, o) { Activity( name: activity.name, input: activity.input, input_codec: activity.input_codec, output_codec: activity.output_codec, runner: activity.runner, retry_policy: activity.retry_policy, timeout: activity.timeout, heartbeat: Some(heartbeat_interval), ) } /// Return the activity name used by the engine dispatch boundary. pub fn name(activity: Activity(i, o)) -> String { activity.name } /// Return the typed input captured by the activity value. pub fn input(activity: Activity(i, o)) -> i { activity.input } /// Return the typed input codec captured by the activity value. pub fn input_codec(activity: Activity(i, o)) -> codec.Codec(i) { activity.input_codec } /// Return the typed output codec captured by the activity value. pub fn output_codec(activity: Activity(i, o)) -> codec.Codec(o) { activity.output_codec } /// Return the typed runner captured by the activity value. pub fn runner( activity: Activity(i, o), ) -> fn(i) -> Result(o, error.ActivityError) { activity.runner } /// Return the explicitly attached retry policy, if one exists. pub fn retry_policy(activity: Activity(i, o)) -> Option(RetryPolicy) { activity.retry_policy } /// Return the explicitly attached timeout duration, if one exists. pub fn timeout_duration(activity: Activity(i, o)) -> Option(duration.Duration) { activity.timeout } /// Return the explicitly attached heartbeat interval, if one exists. pub fn heartbeat_interval( activity: Activity(i, o), ) -> Option(duration.Duration) { activity.heartbeat }