use crate::message::Context;
use crate::runtime::{client, lmo, Result, RobomotionError};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value;
use std::marker::PhantomData;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VariableConfig {
#[serde(default)]
pub scope: String,
#[serde(default)]
pub name: Value, }
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InVariable<T> {
#[serde(flatten)]
pub config: VariableConfig,
#[serde(skip)]
_marker: PhantomData<T>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OutVariable<T> {
#[serde(flatten)]
pub config: VariableConfig,
#[serde(skip)]
_marker: PhantomData<T>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OptVariable<T> {
#[serde(flatten)]
pub inner: InVariable<T>,
}
impl<T> InVariable<T>
where
T: DeserializeOwned + Default + Clone,
{
pub fn new(scope: impl Into<String>, name: impl Into<Value>) -> Self {
Self {
config: VariableConfig {
scope: scope.into(),
name: name.into(),
},
_marker: PhantomData,
}
}
pub fn is_nil(&self) -> bool {
self.config.name.is_null()
}
pub async fn get(&self, ctx: &Context) -> Result<T> {
let scope = &self.config.scope;
if scope == "Custom" {
let value = &self.config.name;
if value.is_null() {
return Ok(T::default());
}
return convert_value::<T>(value.clone());
}
if scope == "Message" || scope == "AI" {
let name = match &self.config.name {
Value::String(s) => s.clone(),
_ => return Err(RobomotionError::Variable("Invalid variable name".to_string())),
};
let mut value = ctx.get(&name);
if value.is_none() && scope == "AI" {
if let Some(msg_type) = ctx.get("__message_type__") {
if msg_type.as_str() == Some("tool_request") {
if let Some(params) = ctx.get("__parameters__") {
if let Some(obj) = params.as_object() {
value = obj.get(&name).cloned();
}
}
}
}
}
match value {
Some(v) => {
if lmo::is_lmo(&v) {
let lmo_obj = lmo::deserialize_lmo_from_map(
v.as_object()
.ok_or_else(|| RobomotionError::Lmo("Invalid LMO".to_string()))?,
)
.await?;
return convert_value::<T>(
lmo_obj.data.unwrap_or(Value::Null),
);
}
convert_value::<T>(v)
}
None => Ok(T::default()),
}
} else {
let name = match &self.config.name {
Value::String(s) => s.clone(),
_ => return Err(RobomotionError::Variable("Invalid variable name".to_string())),
};
let payload = ctx.get_raw()?;
let value = client::get_variable(scope, &name, &payload).await?;
if lmo::is_lmo(&value) {
let lmo_obj = lmo::deserialize_lmo_from_map(
value
.as_object()
.ok_or_else(|| RobomotionError::Lmo("Invalid LMO".to_string()))?,
)
.await?;
return convert_value::<T>(lmo_obj.data.unwrap_or(Value::Null));
}
convert_value::<T>(value)
}
}
}
impl<T> OutVariable<T>
where
T: Serialize,
{
pub async fn set(&self, ctx: &Context, value: T) -> Result<()> {
let scope = &self.config.scope;
let name = match &self.config.name {
Value::String(s) if !s.is_empty() => s.clone(),
_ => return Err(RobomotionError::Variable("Empty message object".to_string())),
};
if scope == "Message" || scope == "AI" {
if client::is_lmo_capable().await {
if let Some(lmo) = lmo::serialize_lmo(&value).await? {
ctx.set(&name, serde_json::to_value(&lmo)?)?;
return Ok(());
}
}
ctx.set(&name, serde_json::to_value(&value)?)?;
Ok(())
} else {
let serialized = serde_json::to_value(&value)?;
if client::is_lmo_capable().await {
if let Some(lmo) = lmo::serialize_lmo(&value).await? {
return client::set_variable(scope, &name, serde_json::to_value(&lmo)?).await;
}
}
client::set_variable(scope, &name, serialized).await
}
}
}
impl<T> OptVariable<T>
where
T: DeserializeOwned + Default + Clone,
{
pub fn is_nil(&self) -> bool {
self.inner.is_nil()
}
pub async fn get(&self, ctx: &Context) -> Result<Option<T>> {
if self.is_nil() {
return Ok(None);
}
self.inner.get(ctx).await.map(Some)
}
}
fn convert_value<T: DeserializeOwned + Default>(value: Value) -> Result<T> {
match &value {
Value::Null => Ok(T::default()),
Value::String(s) => {
if let Ok(result) = serde_json::from_value::<T>(value.clone()) {
return Ok(result);
}
serde_json::from_value(Value::String(s.clone())).map_err(|e| e.into())
}
Value::Number(n) => {
if let Ok(result) = serde_json::from_value::<T>(value.clone()) {
return Ok(result);
}
serde_json::from_value(Value::String(n.to_string())).map_err(|e| e.into())
}
Value::Bool(b) => {
if let Ok(result) = serde_json::from_value::<T>(value.clone()) {
return Ok(result);
}
serde_json::from_value(Value::String(b.to_string())).map_err(|e| e.into())
}
_ => serde_json::from_value(value).map_err(|e| e.into()),
}
}