use koicore::WriterConfig;
use koicore::command::Command;
use koicore::writer::{FormatterOptions, ParamFormatSelector, Writer};
use std::collections::HashMap;
use std::ffi::{CStr, c_char, c_void};
use std::fs::File;
use std::io::{BufWriter, Write};
use std::ptr;
pub use self::config::{
KoiCommandOption, KoiFormatterOptions, KoiParamFormatSelector, KoiParamOption,
KoiWriterConfig,
};
use self::output::{
CustomWriterOutput, KoiStringOutput, KoiWriterOutputVTable, SharedBufferWriter,
};
use crate::command::KoiCommand;
pub mod config;
pub mod output;
#[repr(C)]
pub struct KoiWriter {
inner: Writer<Box<dyn Write + Send>>,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_NewFromVTable(
vtable: *const KoiWriterOutputVTable,
user_data: *mut c_void,
config: *const KoiWriterConfig,
) -> *mut KoiWriter {
if vtable.is_null() || config.is_null() {
return ptr::null_mut();
}
let output = unsafe { CustomWriterOutput::new(vtable, user_data) };
let config = unsafe { WriterConfig::from(&*config) };
let boxed_output: Box<dyn Write + Send> = Box::new(output);
let writer = Writer::new(boxed_output, config);
Box::into_raw(Box::new(KoiWriter { inner: writer }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_NewFromFile(
path: *const c_char,
config: *const KoiWriterConfig,
) -> *mut KoiWriter {
if path.is_null() || config.is_null() {
return ptr::null_mut();
}
let path_str = match unsafe { CStr::from_ptr(path) }.to_str() {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let file = match File::create(path_str) {
Ok(f) => f,
Err(_) => return ptr::null_mut(),
};
let config = unsafe { WriterConfig::from(&*config) };
let boxed_output: Box<dyn Write + Send> = Box::new(BufWriter::new(file));
let writer = Writer::new(boxed_output, config);
Box::into_raw(Box::new(KoiWriter { inner: writer }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_NewFromStringOutput(
output: *mut KoiStringOutput,
config: *const KoiWriterConfig,
) -> *mut KoiWriter {
if output.is_null() || config.is_null() {
return ptr::null_mut();
}
let output_obj = unsafe { &*output };
let buffer_writer = SharedBufferWriter {
buffer: output_obj.buffer.clone(),
};
let config = unsafe { WriterConfig::from(&*config) };
let boxed_output: Box<dyn Write + Send> = Box::new(buffer_writer);
let writer = Writer::new(boxed_output, config);
Box::into_raw(Box::new(KoiWriter { inner: writer }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_Del(writer: *mut KoiWriter) {
if !writer.is_null() {
unsafe {
drop(Box::from_raw(writer));
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_WriteCommand(
writer: *mut KoiWriter,
command: *const KoiCommand,
) -> i32 {
if writer.is_null() || command.is_null() {
return -1;
}
let writer = unsafe { &mut *writer };
let command = unsafe { &*(command as *const Command) };
match writer.inner.write_command(command) {
Ok(_) => 0,
Err(_) => -2,
}
}
unsafe fn parse_param_options(
ptr: *const KoiParamOption,
) -> HashMap<ParamFormatSelector, FormatterOptions> {
unsafe {
let mut map = HashMap::new();
if ptr.is_null() {
return map;
}
let mut current = ptr;
while !(*current).selector.name.is_null() || (*current).selector.is_position {
let sel = (*current).selector;
if sel.name.is_null() && !sel.is_position {
break;
}
let selector = if sel.is_position {
ParamFormatSelector::Position(sel.position)
} else {
let name_str = CStr::from_ptr(sel.name).to_string_lossy().into_owned();
ParamFormatSelector::Name(name_str)
};
map.insert(selector, (*current).options.into());
current = current.add(1);
}
map
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_WriteCommandWithOptions(
writer: *mut KoiWriter,
command: *const KoiCommand,
options: *const KoiFormatterOptions,
param_options: *const KoiParamOption,
) -> i32 {
if writer.is_null() || command.is_null() {
return -1;
}
let writer = unsafe { &mut *writer };
let command = unsafe { &*(command as *const Command) };
let options: Option<FormatterOptions> = if options.is_null() {
None
} else {
Some(unsafe { (*options).into() })
};
let param_options_map = if param_options.is_null() {
None
} else {
Some(unsafe { parse_param_options(param_options) })
};
let param_options_ref_map =
param_options_map
.as_ref()
.map(|m| m.iter().map(|(k, v)| (k.clone(), v)).collect());
let options_ref = options.as_ref();
match writer
.inner
.write_command_with_options(command, options_ref, param_options_ref_map.as_ref())
{
Ok(_) => 0,
Err(_) => -2,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_IncIndent(writer: *mut KoiWriter) {
if !writer.is_null() {
let writer = unsafe { &mut *writer };
writer.inner.inc_indent();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_DecIndent(writer: *mut KoiWriter) {
if !writer.is_null() {
let writer = unsafe { &mut *writer };
writer.inner.dec_indent();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_GetIndent(writer: *const KoiWriter) -> usize {
if !writer.is_null() {
let writer = unsafe { &*writer };
writer.inner.get_indent()
} else {
0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn KoiWriter_Newline(writer: *mut KoiWriter) -> i32 {
if !writer.is_null() {
let writer = unsafe { &mut *writer };
match writer.inner.newline() {
Ok(_) => 0,
Err(_) => -2,
}
} else {
-1
}
}