diff --git a/Cargo.lock b/Cargo.lock index 683dfc86ec..d5393c8f19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1439,9 +1439,11 @@ dependencies = [ name = "datadog-ffe-ffi" version = "0.1.0" dependencies = [ + "anyhow", "build_common", "datadog-ffe", "ddcommon-ffi", + "function_name", ] [[package]] diff --git a/datadog-ffe-ffi/Cargo.toml b/datadog-ffe-ffi/Cargo.toml index ec8304cb1d..c2c3ba2bca 100644 --- a/datadog-ffe-ffi/Cargo.toml +++ b/datadog-ffe-ffi/Cargo.toml @@ -20,7 +20,9 @@ cbindgen = ["build_common/cbindgen", "ddcommon-ffi/cbindgen"] build_common = { path = "../build-common" } [dependencies] +anyhow = "1.0.93" datadog-ffe = { path = "../datadog-ffe", version = "=0.1.0" } ddcommon-ffi = { path = "../ddcommon-ffi", default-features = false } +function_name = "0.3.0" [dev-dependencies] diff --git a/datadog-ffe-ffi/src/assignment.rs b/datadog-ffe-ffi/src/assignment.rs index 510c3f27a9..52e28157ad 100644 --- a/datadog-ffe-ffi/src/assignment.rs +++ b/datadog-ffe-ffi/src/assignment.rs @@ -2,64 +2,43 @@ // SPDX-License-Identifier: Apache-2.0 use std::ffi::{c_char, CStr}; -use std::mem::MaybeUninit; -use datadog_ffe::rules_based::{get_assignment, now, Assignment, Configuration, EvaluationContext}; -use ddcommon_ffi::{Handle, ToInner, VoidResult}; +use anyhow::ensure; +use function_name::named; -use crate::error::ffe_error; +use datadog_ffe::rules_based::{get_assignment, now, Assignment, Configuration, EvaluationContext}; +use ddcommon_ffi::{wrap_with_ffi_result, Handle, Result, ToInner}; -/// Evaluates a feature flag and returns success/failure via VoidResult -/// If successful, writes the assignment to the output parameter +/// Evaluates a feature flag. /// /// # Safety /// - `config` must be a valid Configuration handle /// - `context` must be a valid EvaluationContext handle /// - `flag_key` must be a valid null-terminated C string -/// - `assignment_out` must point to valid uninitialized memory for a Handle #[no_mangle] +#[named] pub unsafe extern "C" fn ddog_ffe_get_assignment( - mut config: *mut Handle, + mut config: Handle, flag_key: *const c_char, - mut context: *mut Handle, - assignment_out: *mut MaybeUninit>, -) -> VoidResult { - if flag_key.is_null() { - return VoidResult::Err(ffe_error("flag_key cannot be null")); - } - if assignment_out.is_null() { - return VoidResult::Err(ffe_error("assignment_out cannot be null")); - } - - let config_ref = match config.to_inner_mut() { - Ok(c) => c, - Err(e) => return VoidResult::Err(ffe_error(&e.to_string())), - }; + mut context: Handle, +) -> Result> { + wrap_with_ffi_result!({ + ensure!(!flag_key.is_null(), "flag_key must not be NULL"); - let context_ref = match context.to_inner_mut() { - Ok(c) => c, - Err(e) => return VoidResult::Err(ffe_error(&e.to_string())), - }; + let config = config.to_inner_mut()?; + let context = context.to_inner_mut()?; + let flag_key = CStr::from_ptr(flag_key).to_str()?; - let flag_key_str = match CStr::from_ptr(flag_key).to_str() { - Ok(s) => s, - Err(_) => return VoidResult::Err(ffe_error("flag_key must be valid UTF-8")), - }; + let assignment_result = get_assignment(Some(config), flag_key, context, None, now())?; - let assignment_result = - get_assignment(Some(config_ref), flag_key_str, context_ref, None, now()); + let handle = if let Some(assignment) = assignment_result { + Handle::from(assignment) + } else { + Handle::empty() + }; - match assignment_result { - Ok(Some(assignment)) => { - assignment_out.write(MaybeUninit::new(Handle::from(assignment))); - VoidResult::Ok - } - Ok(None) => { - assignment_out.write(MaybeUninit::new(Handle::empty())); - VoidResult::Ok - } - Err(_) => VoidResult::Err(ffe_error("assignment evaluation failed")), - } + Ok(handle) + }) } /// Frees an Assignment handle diff --git a/datadog-ffe-ffi/src/configuration.rs b/datadog-ffe-ffi/src/configuration.rs index d8126c3589..9cc28ec345 100644 --- a/datadog-ffe-ffi/src/configuration.rs +++ b/datadog-ffe-ffi/src/configuration.rs @@ -3,35 +3,31 @@ use std::ffi::{c_char, CStr}; +use anyhow::ensure; +use function_name::named; + use datadog_ffe::rules_based::{Configuration, UniversalFlagConfig}; -use ddcommon_ffi::{Handle, ToInner}; +use ddcommon_ffi::{wrap_with_ffi_result, Handle, Result, ToInner}; /// Creates a new Configuration from JSON bytes /// /// # Safety -/// `json_str` must be a valid null-terminated C string containing valid JSON +/// `json_str` must be a valid C string. #[no_mangle] +#[named] pub unsafe extern "C" fn ddog_ffe_configuration_new( json_str: *const c_char, -) -> Handle { - if json_str.is_null() { - return Handle::empty(); - } - - let json_cstr = match CStr::from_ptr(json_str).to_str() { - Ok(s) => s, - Err(_) => return Handle::empty(), - }; - - let json_bytes = json_cstr.as_bytes().to_vec(); - - match UniversalFlagConfig::from_json(json_bytes) { - Ok(universal_config) => { - let config = Configuration::from_server_response(universal_config); - Handle::from(config) - } - Err(_) => Handle::empty(), - } +) -> Result> { + wrap_with_ffi_result!({ + ensure!(!json_str.is_null(), "json_str must not be NULL"); + + let json_bytes = CStr::from_ptr(json_str).to_bytes().to_vec(); + + let configuration = + Configuration::from_server_response(UniversalFlagConfig::from_json(json_bytes)?); + + Ok(Handle::from(configuration)) + }) } /// Frees a Configuration diff --git a/datadog-ffe-ffi/src/error.rs b/datadog-ffe-ffi/src/error.rs deleted file mode 100644 index 9749cd14b6..0000000000 --- a/datadog-ffe-ffi/src/error.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2025-Present Datadog, Inc. https://www.datadoghq.com/ -// SPDX-License-Identifier: Apache-2.0 - -use ddcommon_ffi::Error; - -pub fn ffe_error(msg: &str) -> Error { - Error::from(msg) -} diff --git a/datadog-ffe-ffi/src/lib.rs b/datadog-ffe-ffi/src/lib.rs index 40858896ae..094a8ab080 100644 --- a/datadog-ffe-ffi/src/lib.rs +++ b/datadog-ffe-ffi/src/lib.rs @@ -9,10 +9,8 @@ mod assignment; mod configuration; -mod error; mod evaluation_context; pub use assignment::*; pub use configuration::*; -pub use error::*; pub use evaluation_context::*;