mocha-rs 0.1.0

Binding for mocha parser an elegant configuration language for both humans and machines
Documentation
pub mod raw;
use std::slice;
use std::ffi::CStr;
use std::os::raw::c_void;

#[derive(Debug)]
pub enum MochaError {
    MissingField,
    DuplicateField,
    RootReference,
    OutOfMemory,
    InvalidCharacter,
    Overflow,
    EndOfStream,
    UnexpectedToken,
    UnexpectedCharacter
}

#[derive(Debug)]
pub enum Value<'a> {
    String(&'a [u8]),
    Ref(Reference<'a>),
    Bool(bool),
    Object(Object),
    Array(Array),
    Float(f64),
    Int(i64),
    Nil,
}

#[derive(Debug)]
pub struct Reference<'a> {
    child: *const c_void,
    pub name: &'a [u8],
    pub index: usize,
}

#[derive(Debug)]
pub struct Field<'a> {
    pub name: &'a [u8],
    pub value: Value<'a>,
}

#[derive(Debug)]
pub struct Array {
    array: *mut c_void,
    pub len: usize,
}

#[derive(Debug)]
pub struct Object {
    obj: *mut c_void,
    pub len: usize,
}

#[derive(Debug)]
pub struct Mocha {
    obj: *mut c_void,
    pub len: usize,
}

impl Drop for Mocha {
    fn drop(&mut self) {
	unsafe { raw::mocha_deinit(&mut raw::mocha_object_t{fields: self.obj, fields_len: self.len}) }
    }
}

impl Mocha {
    pub fn parse(src: &str) -> Result<Self, MochaError> {
	unsafe {
	    let mut object = raw::mocha_object_t{fields: 0 as _, fields_len: 0};
	    let mocha = raw::mocha_nparse(&mut object as _, src.as_ptr() as _, src.len());
	    if let Some(err) = handle_mocha_error(mocha) {
		Err(err)
	    } else {
		Ok(Self {obj: object.fields, len: object.fields_len})
	    }
	}
    }

    pub fn get(&self, index: usize) -> Option<Field> {
	unsafe {
	    if index >= self.len { return None }
	    let field = raw::mocha_field(&raw::mocha_object_t{fields: self.obj, fields_len: self.len} as _, index);
	    match field.type_ {
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_NIL => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
									     value: Value::Nil }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_STRING => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										value: Value::String(CStr::from_ptr(field.value.string).to_bytes()) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_REFERENCE => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										   value: Value::Ref(Reference{child: field.value.reference.child,
													       name: slice::from_raw_parts(field.value.reference.name as _,
																	   field.value.reference.name_len),
													       index: field.value.reference.index}) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_BOOLEAN => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										 value: Value::Bool(if field.value.boolean == 0 { false } else { true }) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_OBJECT => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										value: Value::Object(Object{obj: field.value.object.fields,
													    len: field.value.object.fields_len}) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_ARRAY => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
									       value: Value::Array(Array{array: field.value.array.items,
													 len: field.value.array.items_len}) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_FLOAT64 => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										 value: Value::Float(field.value.float64) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_INTEGER64 => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										 value: Value::Int(field.value.integer64) }),
		_ => None
	    }
	}
    }
}

impl Object {
    pub fn get(&self, index: usize) -> Option<Field> {
	unsafe {
	    if index >= self.len { return None }
	    let field = raw::mocha_field(&raw::mocha_object_t{fields: self.obj, fields_len: self.len} as _, index);
	    match field.type_ {
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_NIL => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
									     value: Value::Nil }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_STRING => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										value: Value::String(CStr::from_ptr(field.value.string).to_bytes()) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_REFERENCE => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										   value: Value::Ref(Reference{child: field.value.reference.child,
													       name: slice::from_raw_parts(field.value.reference.name as _,
																	   field.value.reference.name_len),
													       index: field.value.reference.index}) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_BOOLEAN => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										 value: Value::Bool(if field.value.boolean == 0
												    { false } else { true })}),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_OBJECT => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										value: Value::Object(Object{obj: field.value.object.fields,
													    len: field.value.object.fields_len}) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_ARRAY => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
									       value: Value::Array(Array{array: field.value.array.items,
													 len: field.value.array.items_len}) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_FLOAT64 => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										 value: Value::Float(field.value.float64) }),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_INTEGER64 => Some(Field { name: CStr::from_ptr(field.name).to_bytes(),
										   value: Value::Int(field.value.integer64) }),
		_ => None
	    }
	}
    }
}

impl Array {
    pub fn get(&self, index: usize) -> Option<Value> {
	unsafe {
	    if index >= self.len { return None }
	    let mut value = raw::mocha_value_t{boolean: 0};
	    let type_ = raw::mocha_array(&raw::mocha_array_t{ items: self.array, items_len: self.len}, &mut value as _, index);
	    match type_ {
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_NIL => Some(Value::Nil),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_STRING => Some(Value::String(CStr::from_ptr(value.string).to_bytes())),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_REFERENCE => Some(Value::Ref(Reference{child: value.reference.child,
													       name: slice::from_raw_parts(value.reference.name as _,
																	   value.reference.name_len),
													       index: value.reference.index})),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_BOOLEAN => Some(Value::Bool(if value.boolean == 0 { false } else { true })),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_OBJECT => Some(Value::Object(Object{obj: value.object.fields,
													    len: value.object.fields_len})),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_ARRAY => Some(Value::Array(Array{array: value.array.items, len: value.array.items_len})),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_FLOAT64 => Some(Value::Float(value.float64)),
		raw::mocha_value_type_t_MOCHA_VALUE_TYPE_INTEGER64 => Some(Value::Int(value.integer64)),
		_ => None
	    }
	}
    }
}

impl Reference<'_> {
    pub fn next(&self) -> Option<Self> {
	unsafe {
	    let mut reference = raw::mocha_reference_t{name: 0 as _,
						       name_len: 0,
						       child: self.child,index: 0};
	    let err = raw::mocha_reference_next(&mut reference as _);
	    if err == 0 {
		Some(Self{child: reference.child,
		     name: slice::from_raw_parts(reference.name as _,
						 reference.name_len),
		     index: reference.index})
	    } else {
		None
	    }
	}
    }
}

#[inline(always)]
fn handle_mocha_error(err: raw::mocha_error_t) -> Option<MochaError> {
    match err {
	raw::mocha_error_t_MOCHA_ERROR_NONE => None,
	raw::mocha_error_t_MOCHA_ERROR_MISSING_FIELD => Some(MochaError::MissingField),
	raw::mocha_error_t_MOCHA_ERROR_DUPLICATE_FIELD => Some(MochaError::DuplicateField),
	raw::mocha_error_t_MOCHA_ERROR_ROOT_REFERENCE => Some(MochaError::RootReference),
	raw::mocha_error_t_MOCHA_ERROR_OUT_OF_MEMORY => Some(MochaError::OutOfMemory),
	raw::mocha_error_t_MOCHA_ERROR_INVALID_CHARACTER => Some(MochaError::InvalidCharacter),
	raw::mocha_error_t_MOCHA_ERROR_OVERFLOW => Some(MochaError::Overflow),
	raw::mocha_error_t_MOCHA_ERROR_END_OF_STREAM => Some(MochaError::EndOfStream),
	raw::mocha_error_t_MOCHA_ERROR_UNEXPECTED_TOKEN => Some(MochaError::UnexpectedToken),
	raw::mocha_error_t_MOCHA_ERROR_UNEXPECTED_CHARACTER => Some(MochaError::UnexpectedCharacter),
	_ => None,
    }
}