Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions datadog-ffe-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
65 changes: 22 additions & 43 deletions datadog-ffe-ffi/src/assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Assignment>
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_ffe_get_assignment(
mut config: *mut Handle<Configuration>,
mut config: Handle<Configuration>,
flag_key: *const c_char,
mut context: *mut Handle<EvaluationContext>,
assignment_out: *mut MaybeUninit<Handle<Assignment>>,
) -> 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<EvaluationContext>,
) -> Result<Handle<Assignment>> {
wrap_with_ffi_result!({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap_with_ffi_result is definitely cleaner and more consistent with the other FFIs!

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
Expand Down
38 changes: 17 additions & 21 deletions datadog-ffe-ffi/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Configuration> {
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<Handle<Configuration>> {
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
Expand Down
8 changes: 0 additions & 8 deletions datadog-ffe-ffi/src/error.rs

This file was deleted.

2 changes: 0 additions & 2 deletions datadog-ffe-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Loading