From 6044d2240b8a11eead5bd6598b4d1262a0c0ccb2 Mon Sep 17 00:00:00 2001 From: Stefan Date: Thu, 31 Jul 2025 01:00:07 +0200 Subject: [PATCH] fix: restore interfaces --- bindings/uniffi/Cargo.toml | 2 +- bindings/uniffi/build.gradle.kts | 4 +- .../io/gorules/zen_engine/JsonBuffer.java | 20 +++ .../gorules/zen_engine/kotlin/JsonBuffer.kt | 11 ++ bindings/uniffi/src/custom_node.rs | 17 ++- bindings/uniffi/src/decision.rs | 8 +- bindings/uniffi/src/engine.rs | 13 +- bindings/uniffi/src/error.rs | 9 ++ bindings/uniffi/src/expression.rs | 52 +++---- bindings/uniffi/src/loader.rs | 14 +- bindings/uniffi/src/types.rs | 134 ++++++++---------- bindings/uniffi/uniffi.toml | 10 +- 12 files changed, 161 insertions(+), 133 deletions(-) create mode 100644 bindings/uniffi/lib/java/io/gorules/zen_engine/JsonBuffer.java create mode 100644 bindings/uniffi/lib/kotlin/io/gorules/zen_engine/kotlin/JsonBuffer.kt diff --git a/bindings/uniffi/Cargo.toml b/bindings/uniffi/Cargo.toml index 6c84c772..1816b485 100644 --- a/bindings/uniffi/Cargo.toml +++ b/bindings/uniffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zen-uniffi" -version = "0.1.8" +version = "0.1.9" edition = "2024" license = "MIT" publish = false diff --git a/bindings/uniffi/build.gradle.kts b/bindings/uniffi/build.gradle.kts index 45307eb9..45de6658 100644 --- a/bindings/uniffi/build.gradle.kts +++ b/bindings/uniffi/build.gradle.kts @@ -27,7 +27,7 @@ repositories { sourceSets { val java by creating { java { - srcDirs("build/generated/java") + srcDirs("lib/java", "build/generated/java") } resources { srcDirs("build/generated/resources") @@ -39,7 +39,7 @@ sourceSets { val kotlin by creating { kotlin { - srcDirs("build/generated/kotlin") + srcDirs("lib/kotlin", "build/generated/kotlin") } resources { srcDirs("build/generated/resources") diff --git a/bindings/uniffi/lib/java/io/gorules/zen_engine/JsonBuffer.java b/bindings/uniffi/lib/java/io/gorules/zen_engine/JsonBuffer.java new file mode 100644 index 00000000..b38b57ec --- /dev/null +++ b/bindings/uniffi/lib/java/io/gorules/zen_engine/JsonBuffer.java @@ -0,0 +1,20 @@ +package io.gorules.zen_engine; + +import org.jetbrains.annotations.NotNull; + +import java.nio.charset.StandardCharsets; + +public record JsonBuffer( + byte[] value +) { + + public JsonBuffer(String value) { + this(value.getBytes(StandardCharsets.UTF_8)); + } + + @NotNull + @Override + public String toString() { + return new String(value, StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/bindings/uniffi/lib/kotlin/io/gorules/zen_engine/kotlin/JsonBuffer.kt b/bindings/uniffi/lib/kotlin/io/gorules/zen_engine/kotlin/JsonBuffer.kt new file mode 100644 index 00000000..d18ea520 --- /dev/null +++ b/bindings/uniffi/lib/kotlin/io/gorules/zen_engine/kotlin/JsonBuffer.kt @@ -0,0 +1,11 @@ +package io.gorules.zen_engine.kotlin + +@JvmInline +value class JsonBuffer(val value: ByteArray) { + + constructor(value: String) : this(value = value.toByteArray(Charsets.UTF_8)) + + override fun toString(): String { + return value.toString(Charsets.UTF_8) + } +} \ No newline at end of file diff --git a/bindings/uniffi/src/custom_node.rs b/bindings/uniffi/src/custom_node.rs index a24e2775..6f402e7c 100644 --- a/bindings/uniffi/src/custom_node.rs +++ b/bindings/uniffi/src/custom_node.rs @@ -1,7 +1,6 @@ use crate::error::ZenError; -use crate::types::{DecisionNode, JsonBuffer, ZenEngineHandlerRequest, ZenEngineHandlerResponse}; +use crate::types::{DecisionNode, ZenEngineHandlerRequest, ZenEngineHandlerResponse}; use serde_json::Value; -use std::sync::Arc; use uniffi::deps::anyhow::anyhow; use zen_engine::handler::custom_node_adapter::{CustomNodeAdapter, CustomNodeRequest}; use zen_engine::handler::node::{NodeResponse, NodeResult}; @@ -32,7 +31,11 @@ pub struct ZenCustomNodeCallbackWrapper(pub Box); impl CustomNodeAdapter for ZenCustomNodeCallbackWrapper { async fn handle(&self, request: CustomNodeRequest) -> NodeResult { - let input = Arc::new(JsonBuffer(request.input.to_value())); + let input = request + .input + .try_into() + .map_err(|err: ZenError| anyhow!(err))?; + let node = DecisionNode::from(request.node); let result = self @@ -41,8 +44,12 @@ impl CustomNodeAdapter for ZenCustomNodeCallbackWrapper { .await .map_err(|err| anyhow!(err.details()))?; - let output: Variable = result.output.to_variable(); - let trace_data: Option = result.trace_data.map(|trace| trace.to_value()); + let output: Variable = result + .output + .try_into() + .map_err(|err: ZenError| anyhow!(err))?; + + let trace_data: Option = result.trace_data.and_then(|trace| trace.try_into().ok()); Ok(NodeResponse { output, trace_data }) } diff --git a/bindings/uniffi/src/decision.rs b/bindings/uniffi/src/decision.rs index 3a9339fe..b94cb937 100644 --- a/bindings/uniffi/src/decision.rs +++ b/bindings/uniffi/src/decision.rs @@ -30,11 +30,11 @@ impl From, + context: JsonBuffer, options: Option, ) -> Result { let options = options.unwrap_or_default(); - let context: Value = context.to_value(); + let context: Value = context.try_into()?; let decision = self.decision.clone(); let evaluation_options = EvaluationOptions { @@ -49,7 +49,7 @@ impl ZenDecision { decision .evaluate_with_opts(context.into(), evaluation_options) .await - .map(|response| ZenEngineResponse::from(response)) + .map(|response| ZenEngineResponse::try_from(response)) .map_err(|err| { ZenError::EvaluationError( serde_json::to_string(&err.as_ref()) @@ -59,7 +59,7 @@ impl ZenDecision { }) }) .await - .map_err(|e| ZenError::EvaluationError(format!("Task failed: {:?}", e)))??; + .map_err(|e| ZenError::EvaluationError(format!("Task failed: {:?}", e)))???; Ok(response) } diff --git a/bindings/uniffi/src/engine.rs b/bindings/uniffi/src/engine.rs index b8176b0c..df796fed 100644 --- a/bindings/uniffi/src/engine.rs +++ b/bindings/uniffi/src/engine.rs @@ -55,11 +55,11 @@ impl ZenEngine { pub async fn evaluate( &self, key: String, - context: Arc, + context: JsonBuffer, options: Option, ) -> Result { let options = options.unwrap_or_default(); - let context: Value = context.to_value(); + let context: Value = context.try_into()?; let engine = self.engine.clone(); let evaluation_options = EvaluationOptions { @@ -74,7 +74,7 @@ impl ZenEngine { engine .evaluate_with_opts(key, context.into(), evaluation_options) .await - .map(|response| ZenEngineResponse::from(response)) + .map(|response| ZenEngineResponse::try_from(response)) .map_err(|err| { ZenError::EvaluationError( serde_json::to_string(&err.as_ref()) @@ -84,15 +84,14 @@ impl ZenEngine { }) }) .await - .map_err(|e| ZenError::EvaluationError(format!("Task failed: {:?}", e)))??; + .map_err(|e| ZenError::EvaluationError(format!("Task failed: {:?}", e)))???; Ok(response) } - pub fn create_decision(&self, content: Arc) -> Result { + pub fn create_decision(&self, content: JsonBuffer) -> Result { let decision = self.engine.create_decision(Arc::new( - serde_json::from_value(content.to_value()) - .map_err(|_| ZenError::JsonDeserializationFailed)?, + serde_json::from_slice(&content.0).map_err(|_| ZenError::JsonDeserializationFailed)?, )); Ok(ZenDecision::from(decision)) diff --git a/bindings/uniffi/src/error.rs b/bindings/uniffi/src/error.rs index 8d19557d..3a9c28eb 100644 --- a/bindings/uniffi/src/error.rs +++ b/bindings/uniffi/src/error.rs @@ -1,5 +1,6 @@ use serde_json::json; use std::fmt::Formatter; +use zen_expression::IsolateError; #[allow(dead_code)] #[derive(Debug, uniffi::Error)] @@ -52,3 +53,11 @@ impl std::fmt::Display for ZenError { self.details().fmt(f) } } + +impl From for ZenError { + fn from(error: IsolateError) -> Self { + ZenError::EvaluationError( + serde_json::to_string(&error).unwrap_or_else(|_| error.to_string()), + ) + } +} diff --git a/bindings/uniffi/src/expression.rs b/bindings/uniffi/src/expression.rs index 2b5ed53d..76cf930a 100644 --- a/bindings/uniffi/src/expression.rs +++ b/bindings/uniffi/src/expression.rs @@ -1,40 +1,33 @@ use crate::error::ZenError; use crate::types::JsonBuffer; -use std::sync::Arc; use zen_expression::expression::{Standard, Unary}; use zen_expression::{Expression, Variable}; #[uniffi::export] pub fn evaluate_expression( expression: String, - context: Option>, + context: Option, ) -> Result { - let ctx: Variable = context.map(|v| v.to_variable()).unwrap_or(Variable::Null); + let ctx: Variable = context + .map(Variable::try_from) + .transpose()? + .unwrap_or(Variable::Null); - Ok( - zen_expression::evaluate_expression(expression.as_str(), ctx) - .map_err(|e| { - ZenError::EvaluationError( - serde_json::to_string(&e).unwrap_or_else(|_| e.to_string()), - ) - })? - .into(), - ) + zen_expression::evaluate_expression(expression.as_str(), ctx).map(JsonBuffer::try_from)? } #[allow(dead_code)] #[uniffi::export] pub fn evaluate_unary_expression( expression: String, - context: Arc, + context: JsonBuffer, ) -> Result { - let ctx: Variable = context.to_variable(); + let ctx: Variable = context.try_into()?; - Ok( - zen_expression::evaluate_unary_expression(expression.as_str(), ctx).map_err(|e| { - ZenError::EvaluationError(serde_json::to_string(&e).unwrap_or_else(|_| e.to_string())) - })?, - ) + Ok(zen_expression::evaluate_unary_expression( + expression.as_str(), + ctx, + )?) } #[derive(uniffi::Object)] @@ -51,13 +44,13 @@ impl ZenExpression { .map(|expression| Self { expression }) } - pub fn evaluate(&self, context: Option>) -> Result { - let ctx: Variable = context.map(|b| b.to_variable()).unwrap_or(Variable::Null); - let res = self.expression.evaluate(ctx).map_err(|e| { - ZenError::EvaluationError(serde_json::to_string(&e).unwrap_or_else(|_| e.to_string())) - }); + pub fn evaluate(&self, context: Option) -> Result { + let ctx: Variable = context + .map(Variable::try_from) + .transpose()? + .unwrap_or(Variable::Null); - Ok(res?.into()) + self.expression.evaluate(ctx).map(JsonBuffer::try_from)? } } @@ -75,11 +68,8 @@ impl ZenExpressionUnary { .map(|expression| Self { expression }) } - pub fn evaluate(&self, context: Arc) -> Result { - let ctx: Variable = context.to_variable(); - - self.expression.evaluate(ctx).map_err(|e| { - ZenError::EvaluationError(serde_json::to_string(&e).unwrap_or_else(|_| e.to_string())) - }) + pub fn evaluate(&self, context: JsonBuffer) -> Result { + let ctx: Variable = context.try_into()?; + Ok(self.expression.evaluate(ctx)?) } } diff --git a/bindings/uniffi/src/loader.rs b/bindings/uniffi/src/loader.rs index 8aba473b..e21b2e07 100644 --- a/bindings/uniffi/src/loader.rs +++ b/bindings/uniffi/src/loader.rs @@ -9,14 +9,14 @@ use zen_engine::model::DecisionContent; #[uniffi::export(callback_interface)] #[async_trait::async_trait] pub trait ZenDecisionLoaderCallback: Send + Sync { - async fn load(&self, key: String) -> Result>, ZenError>; + async fn load(&self, key: String) -> Result, ZenError>; } pub struct NoopDecisionLoader; #[async_trait::async_trait] impl ZenDecisionLoaderCallback for NoopDecisionLoader { - async fn load(&self, _: String) -> Result>, ZenError> { + async fn load(&self, _: String) -> Result, ZenError> { Err(ZenError::Zero) } } @@ -40,10 +40,12 @@ impl DecisionLoader for ZenDecisionLoaderCallbackWrapper { return Err(Box::new(LoaderError::NotFound(key.to_string()))); }; - let decision_content: DecisionContent = serde_json::from_value(json_buffer.to_value()) - .map_err(|e| LoaderError::Internal { - key: key.to_string(), - source: anyhow!(e), + let decision_content: DecisionContent = + serde_json::from_slice(json_buffer.0.as_slice()).map_err(|e| { + LoaderError::Internal { + key: key.to_string(), + source: anyhow!(e), + } })?; Ok(Arc::new(decision_content)) diff --git a/bindings/uniffi/src/types.rs b/bindings/uniffi/src/types.rs index 1c749297..ba2ccd21 100644 --- a/bindings/uniffi/src/types.rs +++ b/bindings/uniffi/src/types.rs @@ -1,71 +1,46 @@ use crate::error::ZenError; use serde_json::Value; use std::collections::HashMap; -use std::ops::Deref; -use std::sync::Arc; use zen_engine::handler::custom_node_adapter::CustomDecisionNode; use zen_engine::{DecisionGraphResponse, DecisionGraphTrace}; use zen_expression::Variable; -#[derive(uniffi::Object)] -pub struct JsonBuffer(pub(crate) Value); +pub struct JsonBuffer(pub Vec); +uniffi::custom_newtype!(JsonBuffer, Vec); -impl From for Value { - fn from(value: JsonBuffer) -> Self { - value.0 +impl TryFrom for Value { + type Error = ZenError; + + fn try_from(value: JsonBuffer) -> Result { + serde_json::from_slice(&value.0).map_err(|_| ZenError::JsonDeserializationFailed) } } -impl From for Variable { - fn from(value: JsonBuffer) -> Variable { - Variable::from(&value.0) +impl TryFrom for Variable { + type Error = ZenError; + + fn try_from(value: JsonBuffer) -> Result { + serde_json::from_slice(&value.0).map_err(|_| ZenError::JsonDeserializationFailed) } } -impl From for JsonBuffer { - fn from(value: Value) -> Self { - JsonBuffer(value) +impl TryFrom for JsonBuffer { + type Error = ZenError; + + fn try_from(value: Value) -> Result { + serde_json::to_vec(&value) + .map(|v| JsonBuffer(v)) + .map_err(|_| ZenError::JsonSerializationFailed) } } -impl From for JsonBuffer { - fn from(var: Variable) -> Self { - JsonBuffer(var.to_value()) - } -} +impl TryFrom for JsonBuffer { + type Error = ZenError; -impl JsonBuffer { - pub fn to_value(&self) -> Value { - self.0.clone() - } - - pub fn to_variable(&self) -> Variable { - Variable::from(&self.0) - } -} - -#[uniffi::export] -impl JsonBuffer { - #[uniffi::constructor] - pub fn from_bytes(bytes: Vec) -> Result { - serde_json::from_slice(&bytes) - .map(JsonBuffer) - .map_err(|_| ZenError::JsonDeserializationFailed) - } - - #[uniffi::constructor] - pub fn from_string(json_str: String) -> Result { - serde_json::from_str(&json_str) - .map(JsonBuffer) - .map_err(|_| ZenError::JsonDeserializationFailed) - } - - pub fn to_bytes(&self) -> Result, ZenError> { - serde_json::to_vec(&self.0).map_err(|_| ZenError::JsonSerializationFailed) - } - - pub fn to_json(&self) -> String { - serde_json::to_string(&self.0).unwrap_or_else(|_| String::from("Failed to serialize JSON")) + fn try_from(var: Variable) -> Result { + serde_json::to_vec(&var) + .map(|v| JsonBuffer(v)) + .map_err(|_| ZenError::JsonSerializationFailed) } } @@ -73,52 +48,59 @@ impl JsonBuffer { pub struct ZenEngineTrace { pub id: String, pub name: String, - pub input: Arc, - pub output: Arc, + pub input: JsonBuffer, + pub output: JsonBuffer, pub performance: Option, - pub trace_data: Option>, + pub trace_data: Option, pub order: u32, } -impl From for ZenEngineTrace { - fn from(value: DecisionGraphTrace) -> Self { - Self { +impl TryFrom for ZenEngineTrace { + type Error = ZenError; + + fn try_from(value: DecisionGraphTrace) -> Result { + Ok(Self { id: value.id, name: value.name, - input: Arc::new(value.input.into()), - output: Arc::new(value.output.into()), + input: JsonBuffer::try_from(value.input)?, + output: JsonBuffer::try_from(value.output)?, performance: value.performance, - trace_data: value.trace_data.map(|data| Arc::new(data.into())), + trace_data: value.trace_data.map(JsonBuffer::try_from).transpose()?, order: value.order, - } + }) } } #[derive(uniffi::Record)] pub struct ZenEngineResponse { pub performance: String, - pub result: Arc, + pub result: JsonBuffer, pub trace: Option>, } -impl From for ZenEngineResponse { - fn from(value: DecisionGraphResponse) -> Self { - Self { +impl TryFrom for ZenEngineResponse { + type Error = ZenError; + + fn try_from(value: DecisionGraphResponse) -> Result { + Ok(Self { performance: value.performance, - result: Arc::new(value.result.into()), - trace: value.trace.map(|opt| { - opt.into_iter() - .map(|(key, value)| (key, ZenEngineTrace::from(value))) - .collect() - }), - } + result: JsonBuffer::try_from(value.result)?, + trace: value + .trace + .map(|opt| { + opt.into_iter() + .map(|(key, value)| Ok((key, ZenEngineTrace::try_from(value)?))) + .collect::>() + }) + .transpose()?, + }) } } #[derive(uniffi::Record)] pub struct ZenEngineHandlerResponse { - pub output: Arc, - pub trace_data: Option>, + pub output: JsonBuffer, + pub trace_data: Option, } #[derive(uniffi::Record)] @@ -126,7 +108,7 @@ pub struct DecisionNode { pub id: String, pub name: String, pub kind: String, - pub config: Arc, + pub config: JsonBuffer, } impl From for DecisionNode { @@ -135,13 +117,13 @@ impl From for DecisionNode { id: value.id, name: value.name, kind: value.kind, - config: Arc::new(value.config.deref().clone().into()), + config: JsonBuffer(serde_json::to_vec(&value.config).unwrap()), } } } #[derive(uniffi::Record)] pub struct ZenEngineHandlerRequest { - pub input: Arc, + pub input: JsonBuffer, pub node: DecisionNode, } diff --git a/bindings/uniffi/uniffi.toml b/bindings/uniffi/uniffi.toml index a3bf0f8a..b84a34c4 100644 --- a/bindings/uniffi/uniffi.toml +++ b/bindings/uniffi/uniffi.toml @@ -3,7 +3,15 @@ package_name = "io.gorules.zen_engine.kotlin" generate_immutable_records = true +[bindings.kotlin.custom_types.JsonBuffer] +into_custom = "JsonBuffer({})" +from_custom = "{}.value" + # Java [bindings.java] package_name = 'io.gorules.zen_engine' -generate_immutable_records = true \ No newline at end of file +generate_immutable_records = true + +[bindings.java.custom_types.JsonBuffer] +into_custom = "{}" +from_custom = "{}" \ No newline at end of file