use std::convert::From;
use std::ffi::CString;
use std::fmt;
use libc::{
c_double,
c_uchar,
c_void,
};
use error::{UclSchemaError, UclSchemaErrorType};
use libucl_bind::*;
use utils;
pub use self::builder::Builder;
pub use self::emitter::Emitter;
pub use self::types::Type;
pub mod types;
pub mod builder;
pub mod emitter;
#[cfg(test)]
mod test;
extern fn append_char(c: c_uchar, _num_chars: usize, ptr: *mut c_void) -> libc::c_int {
assert!(!ptr.is_null());
unsafe {
let tmp = ptr as *mut String;
(*tmp).push(c as char);
}
0
}
extern fn append_len(c: *const c_uchar, len: usize, ptr: *mut c_void) -> libc::c_int {
assert!(!c.is_null());
assert!(!ptr.is_null());
unsafe {
let out = ptr as *mut String;
let slice = std::slice::from_raw_parts(c, len);
(*out).push_str(std::str::from_utf8(slice).unwrap());
}
0
}
extern fn append_int(i: i64, ptr: *mut c_void) -> libc::c_int {
assert!(!ptr.is_null());
unsafe {
let tmp = ptr as *mut String;
let tmp_str = i.to_string();
(*tmp).push_str(tmp_str.as_str());
}
0
}
extern fn append_double(d: c_double, ptr: *mut c_void) -> libc::c_int {
assert!(!ptr.is_null());
unsafe {
let tmp = ptr as *mut String;
let tmp_str = d.to_string();
(*tmp).push_str(tmp_str.as_str());
}
0
}
pub struct Object {
obj: *mut ucl_object_t,
it: ucl_object_iter_t,
typ: Type
}
impl Object {
fn from_cptr(obj: *const ucl_object_t) -> Option<Self> {
if !obj.is_null() {
Some(Object {
obj: unsafe { ucl_object_ref (obj) },
it: std::ptr::null_mut(),
typ: Type::from(unsafe { ucl_object_type(obj) })
})
} else {
None
}
}
#[allow(dead_code)]
fn from_mut_cptr(obj: *mut ucl_object_t) -> Option<Self> {
if !obj.is_null() {
Some(Object {
obj: obj,
it: std::ptr::null_mut(),
typ: Type::from(unsafe { ucl_object_type(obj) })
})
} else {
None
}
}
fn default_emit_funcs() -> ucl_emitter_functions {
ucl_emitter_functions {
ucl_emitter_append_character: Some(append_char),
ucl_emitter_append_len: Some(append_len),
ucl_emitter_append_int: Some(append_int),
ucl_emitter_append_double: Some(append_double),
ucl_emitter_free_func: None,
ud: std::ptr::null_mut(),
}
}
pub fn dump_into(&self, emmiter: Emitter) -> String {
match emmiter.emit(self){
Some(s) => s,
None => String::from("")
}
}
pub fn dump(&self) -> String {
let out: Box<String> = Box::new(String::new());
let mut emit = Self::default_emit_funcs();
unsafe {
emit.ud = std::mem::transmute::<Box<String>, *mut c_void>(out);
ucl_object_emit_full(self.obj, ucl_emitter_t::UCL_EMIT_JSON, &mut emit, std::ptr::null());
let out_final: Box<String> = std::mem::transmute(emit.ud);
return *out_final
}
}
pub fn size(&self) -> usize {
if self.typ == Type::Array {
return unsafe { ucl_array_size(self.obj) }
}
0
}
pub fn at(&self, i: usize) -> Option<Object> {
if self.typ == Type::Array {
unsafe {
let out = ucl_array_find_index(self.obj, i);
return Object::from_cptr(out)
}
}
None
}
pub fn iter_reset(&mut self) {
if !self.it.is_null() {
self.it = unsafe { ucl_object_iterate_reset(self.it, self.obj) }
}
}
pub fn key(&self) -> Option<String> {
utils::to_str(unsafe { ucl_object_key(self.obj) })
}
pub fn get_type(&self) -> Type {
self.typ
}
pub fn as_int(&self) -> Option<i64> {
if self.get_type() != Type::Int { return None }
unsafe {
let out: *mut i64 = &mut 0i64;
let res = ucl_object_toint_safe(self.obj, out);
if res && !out.is_null() {
Some(*out)
} else {
None
}
}
}
pub fn as_float(&self) -> Option<f64> {
if self.get_type() != Type::Float { return None }
unsafe {
let out: *mut f64 = &mut 0f64;
let res = ucl_object_todouble_safe(self.obj, out);
if res && !out.is_null() {
Some(*out)
} else {
None
}
}
}
pub fn as_bool(&self) -> Option<bool> {
if self.get_type() != Type::Boolean { return None }
unsafe {
let out: *mut bool = &mut true;
let res = ucl_object_toboolean_safe(self.obj, out);
if res && !out.is_null() {
Some(*out)
} else {
None
}
}
}
pub fn as_string(&self) -> Option<String> {
if self.get_type() != Type::String { return None }
unsafe {
let out = ucl_object_tostring(self.obj);
utils::to_str(out)
}
}
pub fn fetch<T: AsRef<str>>(&self, key: T) -> Option<Object> {
if self.get_type() != Type::Object { return None }
let k = CString::new(key.as_ref()).unwrap();
unsafe {
let out = ucl_object_lookup(self.obj, k.as_ptr());
Object::from_cptr(out)
}
}
pub fn fetch_path<T: AsRef<str>>(&self, path: T) -> Option<Object> {
if self.get_type() != Type::Object { return None }
let p = CString::new(path.as_ref()).unwrap();
unsafe {
let out = ucl_object_lookup_path(self.obj, p.as_ptr());
Object::from_cptr(out)
}
}
pub fn validate_with_schema(&self, schema: &Object) -> Result<(), UclSchemaError> {
unsafe {
let mut err = ucl_schema_error {
code: ucl_schema_error_code::UCL_SCHEMA_OK,
msg: ['\0' as i8; 128],
obj: std::ptr::null_mut(),
};
if ucl_object_validate(schema.obj,self.obj, &mut err) {
return Ok(())
}
Err(UclSchemaErrorType::from_code(err.code as i32, String::from("")))
}
}
}
impl Iterator for Object {
type Item = super::Object;
fn next(&mut self) -> Option<Self::Item> {
if self.it.is_null() {
self.it = unsafe { ucl_object_iterate_new(self.obj) }
}
if self.typ != Type::Array {
return None
}
let cur = unsafe { ucl_object_iterate_safe (self.it, true) };
if cur.is_null() {
return None
}
super::Object::from_cptr(cur)
}
}
impl Drop for Object {
fn drop(&mut self) {
unsafe {
if !self.it.is_null() { ucl_object_iterate_free(self.it); }
if !self.obj.is_null() { ucl_object_unref(self.obj); }
}
}
}
impl AsRef<Object> for Object {
fn as_ref(&self) -> &Self { self }
}
impl fmt::Debug for Object {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let string = Emitter::JSON.emit(&self);
if string.is_some() {
fmt.write_str(&string.unwrap())
} else {
Err(fmt::Error)
}
}
}