Value Lanes

This page covers the specifics of Value Lanes and does not cover the more general aspects of Lanes. For more general information about lanes, see the Lane page.

Overview

A Value Lane stores a value that can be mutated and retreived. Each time the state is updated, the updated state is sent to all uplinks attached to it. A Value Lane meets the following requirements:

Example: using a Value Lane to store an i32:

use swimos::agent::{
    agent_lifecycle::utility::HandlerContext,
    event_handler::{EventHandler, HandlerActionExt},
    lanes::ValueLane,
    lifecycle, projections, AgentLaneModel,
};

#[projections]
#[derive(AgentLaneModel)]
pub struct ExampleAgent {
    lane: ValueLane<i32>,
}

#[derive(Clone)]
pub struct ExampleLifecycle;

#[lifecycle(ExampleAgent)]
impl ExampleLifecycle {
    #[on_event(lane)]
    pub fn on_event(
        &self,
        context: HandlerContext<ExampleAgent>,
        value: &i32,
    ) -> impl EventHandler<ExampleAgent> {
        let n = *value;
        context.effect(move || {
            println!("Setting value to: {}", n);
        })
    }
}

Use cases

Value Lanes are used for maintaining state and propagating updates to the state immediately. Common usecases are:

Event handler

Value Lanes expose two event handlers. An on_set handler which is provided a reference to the updated state of the lane, and on_event which is provided a reference to the previous and the updated state of the lane.

On Event

The on_event event handler has the following signature for an i32 lane type:

#[on_event(lane_name)]
fn handler(&self, context: HandlerContext<ExampleAgent>, state: &i32) -> impl EventHandler<ExampleAgent> {
    //...
}

Only one may be registered for the lane and it is invoked exactly once after the state of the lane has changed.

On Set

The on_set event handler has the following signature for an i32 lane type:

#[on_set(lane_name)]
fn handler(&self, context: HandlerContext<ExampleAgent>, new_value: &i32, previous_value: Option<i32>) -> impl EventHandler<ExampleAgent> {
    //...
}

Only one may be registered for the lane and it is invoked exactly once when the state of the lane has changed and is invoked after any registered on_event handler has been invoked.

Handler Context Operations

The HandlerContext provided as an argument to lifecycle event handlers provide a set_value function for updating the state of a Value Lane and a get_value for retreiving the state of a Value Lane. The following example demonstrates retreiving and updating the state of a Value Lane:

use swimos::agent::{
    agent_lifecycle::utility::HandlerContext,
    event_handler::{EventHandler, HandlerActionExt},
    lanes::ValueLane,
    lifecycle, projections,
    AgentLaneModel,
};

#[projections]
#[derive(AgentLaneModel)]
pub struct ExampleAgent {
    state: ValueLane<i32>,
    event_count: ValueLane<u32>,
}

#[derive(Clone)]
pub struct ExampleLifecycle;

#[lifecycle(ExampleAgent)]
impl ExampleLifecycle {
    #[on_event(state)]
    pub fn on_event(
        &self,
        context: HandlerContext<ExampleAgent>,
        _state: &i32,
    ) -> impl EventHandler<ExampleAgent> {
        context
            .get_value(ExampleAgent::EVENT_COUNT)
            .and_then(move |event_count: u32| {
                context.set_value(ExampleAgent::EVENT_COUNT, event_count.wrapping_add(1))
            })
    }
}

Subscription

A subscription to a Value Lane can only be achieved via a Value Downlink. An example client for the previous agent example:

use std::time::Duration;

use swimos_client::{
    BasicValueDownlinkLifecycle, DownlinkConfig, RemotePath,
    SwimClientBuilder,
};

#[tokio::main]
async fn main() {
    let (client, task) = SwimClientBuilder::default().build().await;
    let _client_task = tokio::spawn(task);
    let handle = client.handle();

    let path = RemotePath::new("ws://0.0.0.0:65098", "/example/1", "lane");
    let lifecycle = BasicValueDownlinkLifecycle::default()
        .on_event_blocking(|command: &i32| println!("{command:?}"));
    let downlink = handle
        .value_downlink::<String>(path)
        .lifecycle(lifecycle)
        .downlink_config(DownlinkConfig::default())
        .open()
        .await
        .expect("Failed to open downlink");

    for i in 0..10 {
        downlink.set(i).await.unwrap();
        tokio::time::sleep(Duration::from_millis(100)).await;
    }

    tokio::signal::ctrl_c()
        .await
        .expect("Failed to listen for ctrl-c.");
}

Further reading: Downlinks

Try It Yourself

A standalone project that demonstrates Value Lanes is available here.