use std::ffi::c_char;
use std::sync::Arc;
use blazen_uniffi::errors::BlazenError as InnerError;
use blazen_uniffi::pipeline::{Pipeline as InnerPipeline, PipelineBuilder as InnerPipelineBuilder};
use crate::error::BlazenError;
use crate::future::BlazenFuture;
use crate::runtime::runtime;
use crate::string::{alloc_cstring, cstr_to_str};
use crate::workflow::BlazenWorkflow;
use crate::workflow_records::BlazenWorkflowResult;
unsafe fn write_error(out_err: *mut *mut BlazenError, e: InnerError) -> i32 {
if !out_err.is_null() {
unsafe {
*out_err = BlazenError::from(e).into_ptr();
}
}
-1
}
unsafe fn write_internal_error(out_err: *mut *mut BlazenError, msg: &str) -> i32 {
unsafe {
write_error(
out_err,
InnerError::Internal {
message: msg.into(),
},
)
}
}
unsafe fn take_workflow(wf: *mut BlazenWorkflow) -> Arc<blazen_uniffi::workflow::Workflow> {
let boxed = unsafe { Box::from_raw(wf) };
boxed.0
}
pub struct BlazenPipelineBuilder(pub(crate) Arc<InnerPipelineBuilder>);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_new(
name: *const c_char,
) -> *mut BlazenPipelineBuilder {
let Some(name) = (unsafe { cstr_to_str(name) }) else {
return std::ptr::null_mut();
};
let inner = InnerPipelineBuilder::new(name.to_owned());
Box::into_raw(Box::new(BlazenPipelineBuilder(inner)))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_add_workflow(
builder: *mut BlazenPipelineBuilder,
workflow: *mut BlazenWorkflow,
out_err: *mut *mut BlazenError,
) -> i32 {
if builder.is_null() {
if !workflow.is_null() {
drop(unsafe { Box::from_raw(workflow) });
}
return unsafe { write_internal_error(out_err, "null builder pointer") };
}
if workflow.is_null() {
return unsafe { write_internal_error(out_err, "null workflow pointer") };
}
let wf_arc = unsafe { take_workflow(workflow) };
let builder = unsafe { &*builder };
let inner = Arc::clone(&builder.0);
match inner.add_workflow(wf_arc) {
Ok(_) => 0,
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_stage(
builder: *mut BlazenPipelineBuilder,
name: *const c_char,
workflow: *mut BlazenWorkflow,
out_err: *mut *mut BlazenError,
) -> i32 {
if builder.is_null() {
if !workflow.is_null() {
drop(unsafe { Box::from_raw(workflow) });
}
return unsafe { write_internal_error(out_err, "null builder pointer") };
}
if workflow.is_null() {
return unsafe { write_internal_error(out_err, "null workflow pointer") };
}
let Some(name) = (unsafe { cstr_to_str(name) }) else {
drop(unsafe { Box::from_raw(workflow) });
return unsafe { write_internal_error(out_err, "name not valid UTF-8") };
};
let name = name.to_owned();
let wf_arc = unsafe { take_workflow(workflow) };
let builder = unsafe { &*builder };
let inner = Arc::clone(&builder.0);
match inner.stage(name, wf_arc) {
Ok(_) => 0,
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_parallel(
builder: *mut BlazenPipelineBuilder,
name: *const c_char,
branch_names: *const *const c_char,
branch_count: usize,
workflows: *mut *mut BlazenWorkflow,
workflow_count: usize,
wait_all: bool,
out_err: *mut *mut BlazenError,
) -> i32 {
let mut taken_workflows: Vec<Arc<blazen_uniffi::workflow::Workflow>> =
Vec::with_capacity(workflow_count);
if !workflows.is_null() {
for i in 0..workflow_count {
let wf_ptr = unsafe { *workflows.add(i) };
if wf_ptr.is_null() {
continue;
}
taken_workflows.push(unsafe { take_workflow(wf_ptr) });
}
}
if builder.is_null() {
return unsafe { write_internal_error(out_err, "null builder pointer") };
}
if branch_count != workflow_count {
return unsafe {
write_internal_error(
out_err,
"branch_names length does not match workflows length",
)
};
}
if name.is_null() {
return unsafe { write_internal_error(out_err, "null name pointer") };
}
let Some(name) = (unsafe { cstr_to_str(name) }) else {
return unsafe { write_internal_error(out_err, "name not valid UTF-8") };
};
let name = name.to_owned();
if branch_count > 0 && branch_names.is_null() {
return unsafe { write_internal_error(out_err, "null branch_names pointer") };
}
let mut names: Vec<String> = Vec::with_capacity(branch_count);
for i in 0..branch_count {
let s_ptr = unsafe { *branch_names.add(i) };
let Some(s) = (unsafe { cstr_to_str(s_ptr) }) else {
return unsafe { write_internal_error(out_err, "branch name not valid UTF-8") };
};
names.push(s.to_owned());
}
let builder = unsafe { &*builder };
let inner = Arc::clone(&builder.0);
match inner.parallel(name, names, taken_workflows, wait_all) {
Ok(_) => 0,
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_timeout_per_stage_ms(
builder: *mut BlazenPipelineBuilder,
millis: u64,
out_err: *mut *mut BlazenError,
) -> i32 {
if builder.is_null() {
return unsafe { write_internal_error(out_err, "null builder pointer") };
}
let builder = unsafe { &*builder };
let inner = Arc::clone(&builder.0);
match inner.timeout_per_stage_ms(millis) {
Ok(_) => 0,
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_total_timeout_ms(
builder: *mut BlazenPipelineBuilder,
millis: u64,
out_err: *mut *mut BlazenError,
) -> i32 {
if builder.is_null() {
return unsafe { write_internal_error(out_err, "null builder pointer") };
}
let builder = unsafe { &*builder };
let inner = Arc::clone(&builder.0);
match inner.total_timeout_ms(millis) {
Ok(_) => 0,
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_build(
builder: *mut BlazenPipelineBuilder,
out_pipeline: *mut *mut BlazenPipeline,
out_err: *mut *mut BlazenError,
) -> i32 {
if builder.is_null() {
return unsafe { write_internal_error(out_err, "null builder pointer") };
}
let builder = unsafe { &*builder };
let inner = Arc::clone(&builder.0);
match inner.build() {
Ok(pipeline) => {
if !out_pipeline.is_null() {
unsafe {
*out_pipeline = Box::into_raw(Box::new(BlazenPipeline(pipeline)));
}
}
0
}
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_builder_free(builder: *mut BlazenPipelineBuilder) {
if builder.is_null() {
return;
}
drop(unsafe { Box::from_raw(builder) });
}
pub struct BlazenPipeline(pub(crate) Arc<InnerPipeline>);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_run_blocking(
pipe: *const BlazenPipeline,
input_json: *const c_char,
out_result: *mut *mut BlazenWorkflowResult,
out_err: *mut *mut BlazenError,
) -> i32 {
if pipe.is_null() || input_json.is_null() {
return unsafe { write_internal_error(out_err, "null pointer argument") };
}
let pipe = unsafe { &*pipe };
let input = match unsafe { cstr_to_str(input_json) } {
Some(s) => s.to_owned(),
None => return unsafe { write_internal_error(out_err, "input_json not valid UTF-8") },
};
let inner = Arc::clone(&pipe.0);
match runtime().block_on(async move { inner.run(input).await }) {
Ok(result) => {
if !out_result.is_null() {
unsafe {
*out_result = BlazenWorkflowResult::from(result).into_ptr();
}
}
0
}
Err(e) => unsafe { write_error(out_err, e) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_run(
pipe: *const BlazenPipeline,
input_json: *const c_char,
) -> *mut BlazenFuture {
if pipe.is_null() || input_json.is_null() {
return std::ptr::null_mut();
}
let pipe = unsafe { &*pipe };
let input = match unsafe { cstr_to_str(input_json) } {
Some(s) => s.to_owned(),
None => return std::ptr::null_mut(),
};
let inner = Arc::clone(&pipe.0);
BlazenFuture::spawn(async move { inner.run(input).await })
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_stage_names_count(pipe: *const BlazenPipeline) -> usize {
if pipe.is_null() {
return 0;
}
let pipe = unsafe { &*pipe };
Arc::clone(&pipe.0).stage_names().len()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_stage_names_get(
pipe: *const BlazenPipeline,
idx: usize,
) -> *mut c_char {
if pipe.is_null() {
return std::ptr::null_mut();
}
let pipe = unsafe { &*pipe };
Arc::clone(&pipe.0)
.stage_names()
.get(idx)
.map_or(std::ptr::null_mut(), |name| alloc_cstring(name))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn blazen_pipeline_free(pipe: *mut BlazenPipeline) {
if pipe.is_null() {
return;
}
drop(unsafe { Box::from_raw(pipe) });
}