#![cfg_attr(feature = "cargo-clippy", allow(not_unsafe_ptr_arg_deref))]
use libc::c_char;
use std::ffi::CStr;
use std::path::Path;
use std::fs::File;
use std::io::prelude::*;
use rvs;
use crate::context::Context;
use crate::error::Error;
use crate::error::ErrorKind;
type SequenceHandleRaw = u32;
struct SequenceHandle(SequenceHandleRaw);
impl SequenceHandle {
pub fn to_raw(&self) -> SequenceHandleRaw {
self.0
}
}
impl Into<usize> for SequenceHandle {
fn into(self) -> usize {
(self.to_raw() - 1) as usize
}
}
impl From<usize> for SequenceHandle {
fn from(index: usize) -> SequenceHandle {
SequenceHandle((index + 1) as u32)
}
}
#[no_mangle]
pub extern "C" fn rvs_context_new(
search_path: *const c_char,
seed: u32,
error: *mut Error,
) -> *mut Context {
let c_str = unsafe { CStr::from_ptr(search_path) };
let r_str = c_str.to_str().unwrap();
let search_path = match rvs::SearchPath::from_string(r_str) {
Ok(search_path) => search_path,
Err(e) => {
if !error.is_null() {
unsafe { *error = Error::new(ErrorKind::Io(e)) }
}
Default::default()
}
};
let seed = rvs::Seed::from_u32(seed);
Box::into_raw(Box::new(Context::new(search_path, seed)))
}
#[no_mangle]
pub extern "C" fn rvs_parse(context: *mut Context, s: *const c_char, error: *mut Error) {
assert!(!context.is_null());
assert!(!s.is_null());
let c_str = unsafe { CStr::from_ptr(s) };
let r_str = c_str.to_str().unwrap();
let context = unsafe { &mut *context };
for entry in r_str.split(';') {
if !entry.is_empty() {
let is_file = entry.ends_with(".rvs");
let parser_string = if is_file {
let path = Path::new(&entry);
let path = match context.find_file(path) {
Ok(path) => path,
Err(e) => {
if !error.is_null() {
unsafe {
*error = Error::new(ErrorKind::Io(e));
}
}
return;
}
};
let mut file = match File::open(&path) {
Err(e) => {
if !error.is_null() {
unsafe {
*error = Error::new(ErrorKind::Io(e));
}
}
return;
}
Ok(file) => file,
};
let mut contents = String::new();
if let Err(e) = file.read_to_string(&mut contents) {
if !error.is_null() {
unsafe {
*error = Error::new(ErrorKind::Io(e));
}
}
return;
};
contents
} else {
entry.to_owned() + ";"
};
if let Err(e) = context.parse(&parser_string) {
if !error.is_null() {
unsafe { *error = Error::new(From::from(e)) }
}
}
}
}
}
#[no_mangle]
pub extern "C" fn rvs_model_new() -> *mut rvs::Model {
Box::into_raw(Box::new(rvs::Model::new()))
}
#[no_mangle]
pub extern "C" fn rvs_transform(context: *mut Context, model: *mut rvs::Model, error: *mut Error) {
assert!(!context.is_null());
assert!(!model.is_null());
let context_deref = unsafe { &mut *context };
let mut model = unsafe { &mut *model };
if let Err(e) = context_deref.transform(&mut model) {
if !error.is_null() {
unsafe { *error = Error::new(From::from(e)) }
}
}
unsafe { Box::from_raw(context) };
}
#[no_mangle]
pub extern "C" fn rvs_context_free(context: *mut Context) {
assert!(!context.is_null());
unsafe {
Box::from_raw(context);
}
}
#[no_mangle]
pub extern "C" fn rvs_model_free(model: *mut rvs::Model) {
assert!(!model.is_null());
unsafe {
Box::from_raw(model);
}
}
#[no_mangle]
pub extern "C" fn rvs_get(model: *mut rvs::Model, name: *const c_char) -> SequenceHandleRaw {
assert!(!model.is_null());
assert!(!name.is_null());
let name_cstr = unsafe { CStr::from_ptr(name) };
let name_rstr = name_cstr.to_str().unwrap();
let model = unsafe { &mut *model };
if let Some(index) = model.get_variable_index(name_rstr) {
SequenceHandle::from(index).to_raw()
} else {
0
}
}
#[no_mangle]
pub extern "C" fn rvs_next(model: *mut rvs::Model, handle: SequenceHandleRaw) -> u32 {
assert!(!model.is_null());
let model = unsafe { &mut *model };
let handle = SequenceHandle(handle);
match model.get_variable_by_index(handle.into()) {
Some(variable) => variable.borrow_mut().next(),
None => 0,
}
}
#[no_mangle]
pub extern "C" fn rvs_prev(model: *mut rvs::Model, handle: SequenceHandleRaw) -> u32 {
assert!(!model.is_null());
let model = unsafe { &mut *model };
let handle = SequenceHandle(handle);
match model.get_variable_by_index(handle.into()) {
Some(variable) => variable.borrow().prev(),
None => 0,
}
}
#[no_mangle]
pub extern "C" fn rvs_done(model: *mut rvs::Model, handle: SequenceHandleRaw) -> bool {
assert!(!model.is_null());
let model = unsafe { &mut *model };
let handle = SequenceHandle(handle);
match model.get_variable_by_index(handle.into()) {
Some(variable) => variable.borrow().done(),
None => false,
}
}
#[no_mangle]
pub extern "C" fn rvs_write_definitions(
model: *const rvs::Model,
s: *const c_char,
error: *mut Error,
) {
assert!(!model.is_null());
assert!(!s.is_null());
let c_str = unsafe { CStr::from_ptr(s) };
let r_str = c_str.to_str().unwrap();
let model = unsafe { &*model };
let path = Path::new(r_str);
let mut file = match File::create(&path) {
Err(e) => {
if !error.is_null() {
unsafe {
*error = Error::new(ErrorKind::Io(e));
}
}
return;
}
Ok(file) => file,
};
let variables = format!("{}", model);
if let Err(e) = file.write_all(variables.as_bytes()) {
if !error.is_null() {
unsafe {
*error = Error::new(ErrorKind::Io(e));
}
return;
}
}
}