Skip to content

Commit c4212d7

Browse files
committed
feat: Replace callback parameters with options structs in C bridge
- Replace multiple function parameters with ClientConfigLoadOptions and ClientConfigProfileLoadOptions structs - Remove async callbacks and change functions - Improve documentation to clarify byte array formats - Add ClientConfigOrFail and ClientConfigProfileOrFail result structs
1 parent b1ea496 commit c4212d7

3 files changed

Lines changed: 190 additions & 85 deletions

File tree

core-c-bridge/include/temporal-sdk-core-c-bridge.h

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,52 @@ typedef void (*TemporalCoreClientRpcCallCallback)(void *user_data,
241241
const struct TemporalCoreByteArray *failure_details);
242242

243243
/**
244-
* Callback for client config load operations.
245-
* If success or fail are not null, they must be manually freed when done.
244+
* OrFail result for client config loading operations.
245+
* Either success or fail will be null, but never both.
246+
* If success is not null, it contains JSON-serialized client configuration data.
247+
* If fail is not null, it contains UTF-8 encoded error message.
248+
* The returned ByteArrays must be freed by the caller.
246249
*/
247-
typedef void (*TemporalCoreClientConfigCallback)(void *user_data,
248-
const struct TemporalCoreByteArray *success,
249-
const struct TemporalCoreByteArray *fail);
250+
typedef struct TemporalCoreClientConfigOrFail {
251+
const struct TemporalCoreByteArray *success;
252+
const struct TemporalCoreByteArray *fail;
253+
} TemporalCoreClientConfigOrFail;
254+
255+
/**
256+
* Options for loading client configuration.
257+
*/
258+
typedef struct TemporalCoreClientConfigLoadOptions {
259+
const char *path;
260+
struct TemporalCoreByteArrayRef data;
261+
bool disable_file;
262+
bool config_file_strict;
263+
struct TemporalCoreByteArrayRef env_vars;
264+
} TemporalCoreClientConfigLoadOptions;
265+
266+
/**
267+
* OrFail result for client config profile loading operations.
268+
* Either success or fail will be null, but never both.
269+
* If success is not null, it contains JSON-serialized client configuration profile data.
270+
* If fail is not null, it contains UTF-8 encoded error message.
271+
* The returned ByteArrays must be freed by the caller.
272+
*/
273+
typedef struct TemporalCoreClientConfigProfileOrFail {
274+
const struct TemporalCoreByteArray *success;
275+
const struct TemporalCoreByteArray *fail;
276+
} TemporalCoreClientConfigProfileOrFail;
277+
278+
/**
279+
* Options for loading a specific client configuration profile.
280+
*/
281+
typedef struct TemporalCoreClientConfigProfileLoadOptions {
282+
const char *profile;
283+
const char *path;
284+
struct TemporalCoreByteArrayRef data;
285+
bool disable_file;
286+
bool disable_env;
287+
bool config_file_strict;
288+
struct TemporalCoreByteArrayRef env_vars;
289+
} TemporalCoreClientConfigProfileLoadOptions;
250290

251291
typedef union TemporalCoreMetricAttributeValue {
252292
struct TemporalCoreByteArrayRef string_value;
@@ -780,28 +820,18 @@ void temporal_core_client_rpc_call(struct TemporalCoreClient *client,
780820
TemporalCoreClientRpcCallCallback callback);
781821

782822
/**
783-
* Load all client profiles from given sources
823+
* Load all client profiles from given sources.
824+
* Returns ClientConfigOrFail with either success JSON or error message.
825+
* The returned ByteArrays must be freed by the caller.
784826
*/
785-
void temporal_core_client_config_load(const char *path,
786-
struct TemporalCoreByteArrayRef data,
787-
bool disable_file,
788-
bool config_file_strict,
789-
struct TemporalCoreByteArrayRef env_vars,
790-
void *user_data,
791-
TemporalCoreClientConfigCallback callback);
827+
struct TemporalCoreClientConfigOrFail temporal_core_client_config_load(const struct TemporalCoreClientConfigLoadOptions *options);
792828

793829
/**
794-
* Load a single client profile from given sources with env overrides
830+
* Load a single client profile from given sources with env overrides.
831+
* Returns ClientConfigProfileOrFail with either success JSON or error message.
832+
* The returned ByteArrays must be freed by the caller.
795833
*/
796-
void temporal_core_client_config_profile_load(const char *profile,
797-
const char *path,
798-
struct TemporalCoreByteArrayRef data,
799-
bool disable_file,
800-
bool disable_env,
801-
bool config_file_strict,
802-
struct TemporalCoreByteArrayRef env_vars,
803-
void *user_data,
804-
TemporalCoreClientConfigCallback callback);
834+
struct TemporalCoreClientConfigProfileOrFail temporal_core_client_config_profile_load(const struct TemporalCoreClientConfigProfileLoadOptions *options);
805835

806836
struct TemporalCoreMetricMeter *temporal_core_metric_meter_new(struct TemporalCoreRuntime *runtime);
807837

core-c-bridge/src/envconfig.rs

Lines changed: 136 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,50 @@ use temporal_sdk_core_api::envconfig::{
88
DataSource as CoreDataSource, LoadClientConfigOptions, LoadClientConfigProfileOptions,
99
};
1010

11-
/// Callback for client config load operations.
12-
/// If success or fail are not null, they must be manually freed when done.
13-
pub type ClientConfigCallback = unsafe extern "C" fn(
14-
user_data: *mut libc::c_void,
15-
success: *const ByteArray,
16-
fail: *const ByteArray,
17-
);
11+
/// OrFail result for client config loading operations.
12+
/// Either success or fail will be null, but never both.
13+
/// If success is not null, it contains JSON-serialized client configuration data.
14+
/// If fail is not null, it contains UTF-8 encoded error message.
15+
/// The returned ByteArrays must be freed by the caller.
16+
#[repr(C)]
17+
pub struct ClientConfigOrFail {
18+
pub success: *const ByteArray,
19+
pub fail: *const ByteArray,
20+
}
21+
22+
/// OrFail result for client config profile loading operations.
23+
/// Either success or fail will be null, but never both.
24+
/// If success is not null, it contains JSON-serialized client configuration profile data.
25+
/// If fail is not null, it contains UTF-8 encoded error message.
26+
/// The returned ByteArrays must be freed by the caller.
27+
#[repr(C)]
28+
pub struct ClientConfigProfileOrFail {
29+
pub success: *const ByteArray,
30+
pub fail: *const ByteArray,
31+
}
32+
33+
34+
/// Options for loading client configuration.
35+
#[repr(C)]
36+
pub struct ClientConfigLoadOptions {
37+
pub path: *const libc::c_char,
38+
pub data: ByteArrayRef,
39+
pub disable_file: bool,
40+
pub config_file_strict: bool,
41+
pub env_vars: ByteArrayRef,
42+
}
43+
44+
/// Options for loading a specific client configuration profile.
45+
#[repr(C)]
46+
pub struct ClientConfigProfileLoadOptions {
47+
pub profile: *const libc::c_char,
48+
pub path: *const libc::c_char,
49+
pub data: ByteArrayRef,
50+
pub disable_file: bool,
51+
pub disable_env: bool,
52+
pub config_file_strict: bool,
53+
pub env_vars: ByteArrayRef,
54+
}
1855

1956
// Wrapper types for JSON serialization
2057
#[derive(Serialize)]
@@ -155,97 +192,134 @@ fn parse_env_vars(env_vars: ByteArrayRef) -> Result<Option<HashMap<String, Strin
155192
.map_err(|e| format!("Invalid env vars JSON: {e}"))
156193
}
157194

158-
fn send_result<T: Serialize>(
159-
result: Result<T, String>,
160-
user_data: *mut libc::c_void,
161-
callback: ClientConfigCallback,
162-
) {
163-
match result {
164-
Ok(data) => match serde_json::to_vec(&data) {
165-
Ok(json_bytes) => {
166-
let result = ByteArray::from_vec(json_bytes);
167-
unsafe { callback(user_data, result.into_raw(), std::ptr::null()) };
168-
}
169-
Err(e) => {
170-
let err = ByteArray::from_utf8(format!("Failed to serialize: {e}"));
171-
unsafe { callback(user_data, std::ptr::null(), err.into_raw()) };
172-
}
173-
},
195+
// Simple helper to handle serialization errors consistently
196+
fn serialize_or_error<T: Serialize>(data: T) -> Result<*const ByteArray, *const ByteArray> {
197+
match serde_json::to_vec(&data) {
198+
Ok(json_bytes) => {
199+
let result = ByteArray::from_vec(json_bytes);
200+
Ok(result.into_raw())
201+
}
174202
Err(e) => {
175-
let err = ByteArray::from_utf8(e);
176-
unsafe { callback(user_data, std::ptr::null(), err.into_raw()) };
203+
let err = ByteArray::from_utf8(format!("Failed to serialize: {e}"));
204+
Err(err.into_raw())
177205
}
178206
}
179207
}
180208

181-
/// Load all client profiles from given sources
209+
210+
/// Load all client profiles from given sources.
211+
/// Returns ClientConfigOrFail with either success JSON or error message.
212+
/// The returned ByteArrays must be freed by the caller.
182213
#[unsafe(no_mangle)]
183214
pub extern "C" fn temporal_core_client_config_load(
184-
path: *const libc::c_char,
185-
data: ByteArrayRef,
186-
disable_file: bool,
187-
config_file_strict: bool,
188-
env_vars: ByteArrayRef,
189-
user_data: *mut libc::c_void,
190-
callback: ClientConfigCallback,
191-
) {
215+
options: *const ClientConfigLoadOptions,
216+
) -> ClientConfigOrFail {
217+
if options.is_null() {
218+
let err = ByteArray::from_utf8("Options cannot be null".to_string());
219+
return ClientConfigOrFail {
220+
success: std::ptr::null(),
221+
fail: err.into_raw(),
222+
};
223+
}
224+
192225
let result = || -> Result<ClientConfig, String> {
193-
let config_source = parse_config_source(path, data)?;
194-
let env_vars_map = parse_env_vars(env_vars)?;
226+
let opts = unsafe { &*options };
227+
let config_source = parse_config_source(opts.path, opts.data)?;
228+
let env_vars_map = parse_env_vars(opts.env_vars)?;
195229

196-
let options = LoadClientConfigOptions {
197-
config_source: if disable_file { None } else { config_source },
198-
config_file_strict,
230+
let load_options = LoadClientConfigOptions {
231+
config_source: if opts.disable_file { None } else { config_source },
232+
config_file_strict: opts.config_file_strict,
199233
};
200234

201-
let core_config = envconfig::load_client_config(options, env_vars_map.as_ref())
235+
let core_config = envconfig::load_client_config(load_options, env_vars_map.as_ref())
202236
.map_err(|e| e.to_string())?;
203237

204238
Ok(core_config.into())
205239
};
206240

207-
send_result(result(), user_data, callback);
241+
match result() {
242+
Ok(data) => match serialize_or_error(data) {
243+
Ok(success) => ClientConfigOrFail {
244+
success,
245+
fail: std::ptr::null(),
246+
},
247+
Err(fail) => ClientConfigOrFail {
248+
success: std::ptr::null(),
249+
fail,
250+
},
251+
},
252+
Err(e) => {
253+
let err = ByteArray::from_utf8(e);
254+
ClientConfigOrFail {
255+
success: std::ptr::null(),
256+
fail: err.into_raw(),
257+
}
258+
}
259+
}
208260
}
209261

210-
/// Load a single client profile from given sources with env overrides
262+
/// Load a single client profile from given sources with env overrides.
263+
/// Returns ClientConfigProfileOrFail with either success JSON or error message.
264+
/// The returned ByteArrays must be freed by the caller.
211265
#[unsafe(no_mangle)]
212266
pub extern "C" fn temporal_core_client_config_profile_load(
213-
profile: *const libc::c_char,
214-
path: *const libc::c_char,
215-
data: ByteArrayRef,
216-
disable_file: bool,
217-
disable_env: bool,
218-
config_file_strict: bool,
219-
env_vars: ByteArrayRef,
220-
user_data: *mut libc::c_void,
221-
callback: ClientConfigCallback,
222-
) {
267+
options: *const ClientConfigProfileLoadOptions,
268+
) -> ClientConfigProfileOrFail {
269+
if options.is_null() {
270+
let err = ByteArray::from_utf8("Options cannot be null".to_string());
271+
return ClientConfigProfileOrFail {
272+
success: std::ptr::null(),
273+
fail: err.into_raw(),
274+
};
275+
}
276+
223277
let result = || -> Result<ClientConfigProfile, String> {
224-
let profile_name = if !profile.is_null() {
225-
match unsafe { CStr::from_ptr(profile) }.to_str() {
278+
let opts = unsafe { &*options };
279+
280+
let profile_name = if !opts.profile.is_null() {
281+
match unsafe { CStr::from_ptr(opts.profile) }.to_str() {
226282
Ok(s) => Some(s.to_string()),
227283
Err(e) => return Err(format!("Invalid profile UTF-8: {e}")),
228284
}
229285
} else {
230286
None
231287
};
232288

233-
let config_source = parse_config_source(path, data)?;
234-
let env_vars_map = parse_env_vars(env_vars)?;
289+
let config_source = parse_config_source(opts.path, opts.data)?;
290+
let env_vars_map = parse_env_vars(opts.env_vars)?;
235291

236-
let options = LoadClientConfigProfileOptions {
292+
let load_options = LoadClientConfigProfileOptions {
237293
config_source,
238294
config_file_profile: profile_name,
239-
config_file_strict,
240-
disable_file,
241-
disable_env,
295+
config_file_strict: opts.config_file_strict,
296+
disable_file: opts.disable_file,
297+
disable_env: opts.disable_env,
242298
};
243299

244-
let profile = envconfig::load_client_config_profile(options, env_vars_map.as_ref())
300+
let profile = envconfig::load_client_config_profile(load_options, env_vars_map.as_ref())
245301
.map_err(|e| e.to_string())?;
246302

247303
Ok(profile.into())
248304
};
249305

250-
send_result(result(), user_data, callback);
306+
match result() {
307+
Ok(data) => match serialize_or_error(data) {
308+
Ok(success) => ClientConfigProfileOrFail {
309+
success,
310+
fail: std::ptr::null(),
311+
},
312+
Err(fail) => ClientConfigProfileOrFail {
313+
success: std::ptr::null(),
314+
fail,
315+
},
316+
},
317+
Err(e) => {
318+
let err = ByteArray::from_utf8(e);
319+
ClientConfigProfileOrFail {
320+
success: std::ptr::null(),
321+
fail: err.into_raw(),
322+
}
323+
}
324+
}
251325
}

core-c-bridge/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod tests;
2222
use std::collections::HashMap;
2323

2424
#[repr(C)]
25+
#[derive(Copy, Clone)]
2526
pub struct ByteArrayRef {
2627
pub data: *const u8,
2728
pub size: libc::size_t,

0 commit comments

Comments
 (0)