Skip to main content
Glama
value.rs7.85 kB
use color_eyre::eyre::eyre; use dal::{ AttributeValue, Component, DalContext, attribute::{ path::AttributePath, value::subscription::ValueSubscription, }, workspace_snapshot::node_weight::reason_node_weight::Reason, }; use si_id::AttributeValueId; use crate::{ Result, helpers::{ component::{ self, ComponentKey, }, func::FuncKey, }, }; /// Lookup an attribute value by its key (component, path) pair pub async fn id(ctx: &DalContext, key: impl AttributeValueKey) -> Result<AttributeValueId> { AttributeValueKey::id(ctx, key).await } /// Get or create an attribute value by its key (component, path) pair pub async fn vivify(ctx: &DalContext, key: impl AttributeValueKey) -> Result<AttributeValueId> { AttributeValueKey::vivify(ctx, key).await } /// Set the subscriptions on a value pub async fn subscribe( ctx: &DalContext, subscriber: impl AttributeValueKey, subscription: impl AttributeValueKey, ) -> Result<()> { let subscriber = vivify(ctx, subscriber).await?; let subscription = AttributeValueKey::to_subscription(ctx, subscription).await?; let reason = Reason::new_user_added(ctx); AttributeValue::set_to_subscription(ctx, subscriber, subscription, None, reason).await?; Ok(()) } /// Set the subscriptions on a value pub async fn subscribe_with_custom_function( ctx: &DalContext, subscriber: impl AttributeValueKey, subscription: impl AttributeValueKey, func: impl FuncKey, ) -> Result<()> { let func_id = FuncKey::id(ctx, func).await?; let subscriber = vivify(ctx, subscriber).await?; let subscription = AttributeValueKey::to_subscription(ctx, subscription).await?; let reason = Reason::new_user_added(ctx); AttributeValue::set_to_subscription(ctx, subscriber, subscription, Some(func_id), reason) .await?; Ok(()) } // TODO add a helper to change subscription funcs easily /// Get the value pub async fn get(ctx: &DalContext, av: impl AttributeValueKey) -> Result<serde_json::Value> { let av_id = id(ctx, av).await?; AttributeValue::view(ctx, av_id) .await? .ok_or(eyre!("Attribute missing value")) } /// Check whether the value exists and is set pub async fn has_value(ctx: &DalContext, av: impl AttributeValueKey) -> Result<bool> { match AttributeValueKey::resolve(ctx, av).await? { Some(av_id) => Ok(AttributeValue::view(ctx, av_id).await?.is_some()), None => Ok(false), } } /// Set a value (creates it if it doesn't exist) pub async fn set( ctx: &DalContext, av: impl AttributeValueKey, value: impl Into<serde_json::Value>, ) -> Result<()> { let av_id = vivify(ctx, av).await?; AttributeValue::update(ctx, av_id, Some(value.into())).await?; Ok(()) } /// Check whether the value exists and is set pub async fn is_set(ctx: &DalContext, av: impl AttributeValueKey) -> Result<bool> { match AttributeValueKey::resolve(ctx, av).await? { Some(av_id) => Ok(AttributeValue::component_prototype_id(ctx, av_id) .await? .is_some()), None => Ok(false), } } /// Check whether the value has any subscriptions pub async fn has_subscription(ctx: &DalContext, av: impl AttributeValueKey) -> Result<bool> { match AttributeValueKey::resolve(ctx, av).await? { Some(av_id) => { let subscriptions = AttributeValue::subscriptions(ctx, av_id).await?; match subscriptions { Some(subs) => Ok(!subs.is_empty()), None => Ok(false), } } None => Ok(false), } } /// Unset a value (creates it if it doesn't exist) pub async fn unset(ctx: &DalContext, av: impl AttributeValueKey) -> Result<()> { let av_id = vivify(ctx, av).await?; AttributeValue::update(ctx, av_id, None).await?; Ok(()) } /// /// Things that you can pass as attribute values (id, or (component, path)) /// #[allow(async_fn_in_trait)] pub trait AttributeValueKey { /// /// Get the AttributeValueId for this key /// async fn id(ctx: &DalContext, key: Self) -> Result<AttributeValueId>; /// /// Get the AttributeValueId for this key, or None if it doesn't exist /// async fn resolve(ctx: &DalContext, key: Self) -> Result<Option<AttributeValueId>>; /// /// Get the AttributeValueId for this key, *or create it* if it doesn't exist /// async fn vivify(ctx: &DalContext, key: Self) -> Result<AttributeValueId>; /// /// Turn this into a subscription (resolves component but not av) /// async fn to_subscription(ctx: &DalContext, key: Self) -> Result<ValueSubscription>; } impl AttributeValueKey for AttributeValueId { async fn id(_: &DalContext, key: Self) -> Result<AttributeValueId> { Ok(key) } async fn resolve(_: &DalContext, key: Self) -> Result<Option<AttributeValueId>> { Ok(Some(key)) } async fn vivify(_: &DalContext, key: Self) -> Result<AttributeValueId> { Ok(key) } async fn to_subscription(ctx: &DalContext, key: Self) -> Result<ValueSubscription> { let (root, path) = AttributeValue::path_from_root(ctx, key).await?; let path: &str = &path; AttributeValueKey::to_subscription(ctx, (root, path)).await } } impl AttributeValueKey for ValueSubscription { async fn id(ctx: &DalContext, key: Self) -> Result<AttributeValueId> { key.resolve(ctx) .await? .ok_or(eyre!("Attribute value not found")) } async fn resolve(ctx: &DalContext, key: Self) -> Result<Option<AttributeValueId>> { Ok(key.resolve(ctx).await?) } async fn vivify(ctx: &DalContext, key: Self) -> Result<AttributeValueId> { Ok(key.path.vivify(ctx, key.attribute_value_id).await?) } async fn to_subscription(_: &DalContext, key: Self) -> Result<ValueSubscription> { Ok(key) } } impl AttributeValueKey for (AttributeValueId, &str) { async fn id(ctx: &DalContext, key: Self) -> Result<AttributeValueId> { let sub = AttributeValueKey::to_subscription(ctx, key).await?; AttributeValueKey::id(ctx, sub).await } async fn resolve(ctx: &DalContext, key: Self) -> Result<Option<AttributeValueId>> { let sub = AttributeValueKey::to_subscription(ctx, key).await?; AttributeValueKey::resolve(ctx, sub).await } async fn vivify(ctx: &DalContext, key: Self) -> Result<AttributeValueId> { let sub = AttributeValueKey::to_subscription(ctx, key).await?; AttributeValueKey::vivify(ctx, sub).await } async fn to_subscription(_: &DalContext, key: Self) -> Result<ValueSubscription> { Ok(ValueSubscription { attribute_value_id: key.0, path: AttributePath::from_json_pointer(key.1), }) } } impl<T: ComponentKey> AttributeValueKey for (T, &str) { async fn id(ctx: &DalContext, key: Self) -> Result<AttributeValueId> { let sub = AttributeValueKey::to_subscription(ctx, key).await?; AttributeValueKey::id(ctx, sub).await } async fn resolve(ctx: &DalContext, key: Self) -> Result<Option<AttributeValueId>> { let sub = AttributeValueKey::to_subscription(ctx, key).await?; AttributeValueKey::resolve(ctx, sub).await } async fn vivify(ctx: &DalContext, key: Self) -> Result<AttributeValueId> { let sub = AttributeValueKey::to_subscription(ctx, key).await?; AttributeValueKey::vivify(ctx, sub).await } async fn to_subscription(ctx: &DalContext, key: Self) -> Result<ValueSubscription> { let component_id = component::id(ctx, key.0).await?; let root_id = Component::root_attribute_value_id(ctx, component_id).await?; AttributeValueKey::to_subscription(ctx, (root_id, key.1)).await } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server