Skip to content

Commit

Permalink
Refactor FFI API to use a builder approach.
Browse files Browse the repository at this point in the history
  • Loading branch information
hoolioh committed Sep 10, 2024
1 parent c48d94b commit 3cf03d5
Showing 1 changed file with 238 additions and 20 deletions.
258 changes: 238 additions & 20 deletions data-pipeline-ffi/src/trace_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,75 @@ use ddcommon_ffi::{
};
use std::{ffi::c_char, ptr::NonNull};

#[allow(dead_code)]
#[repr(C)]
pub enum TraceExporterConfigOption<'a> {
Url(CharSlice<'a>),
TracerVersion(CharSlice<'a>),
Language(CharSlice<'a>),
LanguageVersion(CharSlice<'a>),
LanguageInterpreter(CharSlice<'a>),
InputFormat(TraceExporterInputFormat),
OutputFormat(TraceExporterOutputFormat),
ResponseCallback(extern "C" fn(*const c_char)),
}

#[derive(Default)]
pub struct TraceExporterConfig<'a> {
url: Option<CharSlice<'a>>,
tracer_version: Option<CharSlice<'a>>,
language: Option<CharSlice<'a>>,
language_version: Option<CharSlice<'a>>,
language_interpreter: Option<CharSlice<'a>>,
input_format: TraceExporterInputFormat,
output_format: TraceExporterOutputFormat,
agent_response_callback: Option<extern "C" fn(*const c_char)>,
}

/// Create a new TraceExporterConfig instance.
///
/// # Arguments
///
/// * `out_handle` - The handle to write the TraceExporterConfig instance in.
#[no_mangle]
pub unsafe extern "C" fn ddog_trace_exporter_config_new(
out_handle: NonNull<Box<TraceExporterConfig>>,
) -> MaybeError {
out_handle
.as_ptr()
.write(Box::new(TraceExporterConfig::default()));
MaybeError::None
}

/// Set properties on a TraceExporterConfig instance.
///
/// # Arguments
///
/// * `option` - [TraceExporterConfig] enum member.
#[no_mangle]
pub unsafe extern "C" fn ddog_trace_exporter_config_set_option<'a>(
handle: &mut TraceExporterConfig<'a>,
option: TraceExporterConfigOption<'a>,
) -> MaybeError {
match option {
TraceExporterConfigOption::Url(url) => handle.url = Some(url),
TraceExporterConfigOption::TracerVersion(version) => handle.tracer_version = Some(version),
TraceExporterConfigOption::Language(lang) => handle.language = Some(lang),
TraceExporterConfigOption::LanguageVersion(lang_version) => {
handle.language_version = Some(lang_version)
}
TraceExporterConfigOption::LanguageInterpreter(interp) => {
handle.language_interpreter = Some(interp)
}
TraceExporterConfigOption::InputFormat(input) => handle.input_format = input,
TraceExporterConfigOption::OutputFormat(output) => handle.output_format = output,
TraceExporterConfigOption::ResponseCallback(cback) => {
handle.agent_response_callback = Some(cback)
}
}
MaybeError::None
}

/// Create a new TraceExporter instance.
///
/// # Arguments
Expand All @@ -30,30 +99,38 @@ use std::{ffi::c_char, ptr::NonNull};
#[no_mangle]
pub unsafe extern "C" fn ddog_trace_exporter_new(
out_handle: NonNull<Box<TraceExporter>>,
url: CharSlice,
tracer_version: CharSlice,
language: CharSlice,
language_version: CharSlice,
language_interpreter: CharSlice,
input_format: TraceExporterInputFormat,
output_format: TraceExporterOutputFormat,
agent_response_callback: extern "C" fn(*const c_char),
config: NonNull<Box<TraceExporterConfig>>,
) -> MaybeError {
let callback_wrapper = ResponseCallbackWrapper {
response_callback: agent_response_callback,
};
// TODO - handle errors - https://datadoghq.atlassian.net/browse/APMSP-1095
let exporter = TraceExporter::builder()
.set_url(url.to_utf8_lossy().as_ref())
.set_tracer_version(tracer_version.to_utf8_lossy().as_ref())
.set_language(language.to_utf8_lossy().as_ref())
.set_language_version(language_version.to_utf8_lossy().as_ref())
.set_language_interpreter(language_interpreter.to_utf8_lossy().as_ref())
.set_input_format(input_format)
.set_output_format(output_format)
.set_response_callback(Box::new(callback_wrapper))
let mut builder = TraceExporter::builder();
if let Some(url) = config.as_ref().url {
builder = builder.set_url(url.to_utf8_lossy().as_ref());
}
if let Some(tracer_version) = config.as_ref().tracer_version {
builder = builder.set_tracer_version(tracer_version.to_utf8_lossy().as_ref());
}
if let Some(language) = config.as_ref().language {
builder = builder.set_language(language.to_utf8_lossy().as_ref());
}
if let Some(lang_version) = config.as_ref().language_version {
builder = builder.set_language_version(lang_version.to_utf8_lossy().as_ref());
}
if let Some(interpreter) = config.as_ref().language_interpreter {
builder = builder.set_language_interpreter(interpreter.to_utf8_lossy().as_ref());
}
if let Some(callback) = config.as_ref().agent_response_callback {
let wrapper = ResponseCallbackWrapper {
response_callback: callback,
};
builder = builder.set_response_callback(Box::new(wrapper));
}

let exporter = builder
.set_input_format(config.as_ref().input_format)
.set_output_format(config.as_ref().output_format)
.build()
.unwrap();

out_handle.as_ptr().write(Box::new(exporter));
MaybeError::None
}
Expand Down Expand Up @@ -99,3 +176,144 @@ pub unsafe extern "C" fn ddog_trace_exporter_send(
.unwrap_or(String::from(""));
MaybeError::None
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn config_constructor_test() {
let mut config = Box::new(TraceExporterConfig::default());

unsafe {
let ret = ddog_trace_exporter_config_new(NonNull::new_unchecked(&mut config as *mut _));
assert_eq!(MaybeError::None, ret);
}

assert_eq!(config.url, None);
assert_eq!(config.input_format, TraceExporterInputFormat::V04);
assert_eq!(config.language, None);
assert_eq!(config.output_format, TraceExporterOutputFormat::V04);
assert_eq!(config.tracer_version, None);
assert_eq!(config.language_version, None);
assert_eq!(config.language_interpreter, None);
assert_eq!(config.agent_response_callback, None);
}

#[test]
fn config_set_option_test() {
// let foo = "foo"
let foo: &[i8] = &[0x66, 0x6f, 0x6f];
let ptr: *mut *mut TraceExporterConfig = &mut std::ptr::null_mut();

unsafe {
let mut config = std::mem::transmute::<
*mut *mut TraceExporterConfig,
NonNull<Box<TraceExporterConfig>>,
>(ptr);
let ret = ddog_trace_exporter_config_new(config);
assert_eq!(MaybeError::None, ret);

ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::Url(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::InputFormat(TraceExporterInputFormat::V04),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::OutputFormat(TraceExporterOutputFormat::V04),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::Language(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::LanguageVersion(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::TracerVersion(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::LanguageInterpreter(CharSlice::from(foo)),
);

assert_eq!(config.as_ref().url, Some(CharSlice::from(foo)));
assert_eq!(config.as_ref().language, Some(CharSlice::from(foo)));
assert_eq!(config.as_ref().language_version, Some(CharSlice::from(foo)));
assert_eq!(
config.as_ref().language_interpreter,
Some(CharSlice::from(foo))
);
assert_eq!(config.as_ref().tracer_version, Some(CharSlice::from(foo)));
assert_eq!(config.as_ref().input_format, TraceExporterInputFormat::V04);
assert_eq!(
config.as_ref().output_format,
TraceExporterOutputFormat::V04
);
}
}

#[test]
fn exporter_constructor_test() {
// let url = "http://localhost"
let url: &[i8] = &[
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f,
0x73, 0x74,
];
// let foo = "foo"
let foo: &[i8] = &[0x66, 0x6f, 0x6f];

let ptr_config: *mut *mut TraceExporterConfig = &mut std::ptr::null_mut();
let ptr_exporter: *mut *mut TraceExporter = &mut std::ptr::null_mut();

unsafe {
let exporter = std::mem::transmute::<
*mut *mut TraceExporter,
NonNull<Box<TraceExporter>>,
>(ptr_exporter);
let mut config = std::mem::transmute::<
*mut *mut TraceExporterConfig,
NonNull<Box<TraceExporterConfig>>,
>(ptr_config);
let _ = ddog_trace_exporter_config_new(config);

ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::Url(CharSlice::from(url)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::InputFormat(TraceExporterInputFormat::V04),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::OutputFormat(TraceExporterOutputFormat::V04),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::Language(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::LanguageVersion(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::TracerVersion(CharSlice::from(foo)),
);
ddog_trace_exporter_config_set_option(
config.as_mut(),
TraceExporterConfigOption::LanguageInterpreter(CharSlice::from(foo)),
);

let ret = ddog_trace_exporter_new(exporter, config);
assert_eq!(ret, MaybeError::None);
}
}
}

0 comments on commit 3cf03d5

Please sign in to comment.