├── .gitignore ├── .gitmodules ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── fetch_url.rs ├── include.conf ├── load_file.rs ├── static_data.rs └── test.conf ├── libucl-sys ├── .gitignore ├── Cargo.toml ├── LICENSE ├── build.rs └── lib.rs └── src ├── error.rs ├── lib.rs ├── object ├── builder.rs ├── emitter.rs ├── mod.rs ├── test.rs └── types.rs ├── parser.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libucl"] 2 | path = libucl-sys/libucl 3 | url = git://github.com/vstakhov/libucl.git 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: required 3 | rust: 4 | - nightly 5 | - beta 6 | - stable 7 | before_script: 8 | - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH 9 | script: 10 | - | 11 | travis-cargo build && 12 | travis-cargo test && 13 | travis-cargo bench && 14 | travis-cargo doc 15 | after_success: 16 | - travis-cargo --only beta doc-upload 17 | env: 18 | global: 19 | secure: NLZTE6TugA1PvstS5X3F5BGa22LX4csDao6XgvKccfwvV9iOD/oO7E3FGyEDaRfF/4mwbPlyWMRlRxTN5dy7HZ2dHL5KdSlNak/E2nUxFdz5OUXWhnYRuZTi3PUZqk2VLGABaTnCrujAoLVeEuKwTkqDw4GlfFYdIXUBYSdLi80hpJeZ8tQbTi26UvtUi4omac2hSxxNJ0s6gegUooQePeH2WuT4zQCovmYb+FhAoLnjrEjHs1HW6iQOOi4Rb35zDrarApWox3K0LvVnWutCzhTYsAFGhW+j8kTnDILTi805oCizu5m5eyTT9nLsaFYdDA/2LZDrUfwkWWYYMv4yW4tQQZOjoUUbSiOFkasg5aQnHUPfIeLZWEHd29nHTSIDapDfFi7YljhfTimESKxShBebMWajRuuPMPqzV/0kVfuat9L5NgZN+B4TlxvCrbfF1DDVt6PlTqOaCj7PTZ7IEbF/DNk2PzSvODMJ0FqC0Zny3c99uQ22Qn9Rs3rjkiV3XHK4wUMUPL9+h7mXklvq2kjNyYlh3q0pEkNJA8jMG7Wn2c+3D9QRZr0U2agVSzKb0aaM6HJjrcc9Jip+Yiag9dFUPE/r08jKOQlAxx8lVHEZXF9S1qUtkP7eFzZxqrPW7moWKt9XPvrQn8OQdBBUfGEliiCiN2VUbzwaqPzW0os= 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ucl" 3 | version = "0.1.4" 4 | description = "Rust bindings to libucl" 5 | documentation = "http://lukasz.niemier.pl/ucl-rs" 6 | authors = ["Łukasz Niemier "] 7 | license = "MIT" 8 | repository = "https://github.com/hauleth/ucl-rs" 9 | 10 | [features] 11 | default = [] 12 | unstable = [] # in case of nightly build 13 | 14 | [dependencies] 15 | libc = "*" 16 | bitflags = "*" 17 | 18 | [dependencies.libucl-sys] 19 | path = "libucl-sys" 20 | version = "*" 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Łukasz Niemier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust wrapper around [libucl][libucl] 2 | 3 | [![Build Status](https://travis-ci.org/hauleth/ucl-rs.svg)](https://travis-ci.org/hauleth/ucl-rs) 4 | 5 | ## Usage 6 | 7 | ```rust 8 | extern crate ucl; 9 | use ucl::Parser; 10 | 11 | let parser = Parser::new(); 12 | let result = parser.parse(r#"name = "mort"; 13 | section { 14 | nice = true; 15 | server = "http://localhost:6666"; 16 | chunk = 1Gb; 17 | }"#).unwrap(); 18 | 19 | println!("{}", result.fetch_path("section.nice").and_then(|v| v.as_bool())); 20 | ``` 21 | 22 | ## Licence 23 | 24 | Check out [LICENSE](LICENSE) file. 25 | 26 | [libucl]: https://github.com/vstakhov/libucl "Universal configuration library parser" 27 | -------------------------------------------------------------------------------- /examples/fetch_url.rs: -------------------------------------------------------------------------------- 1 | extern crate ucl; 2 | 3 | fn main() { 4 | let parser = ucl::Parser::new(); 5 | 6 | let config = match parser.parse_file("examples/include.conf") { 7 | Ok(conf) => conf, 8 | Err(err) => panic!("{:?}", err) 9 | }; 10 | 11 | println!("{:?}", config.fetch("lol").and_then(|val| val.as_string())); 12 | println!("{:?}", config.fetch_path("placki.duze").and_then(|val| val.as_bool())); 13 | println!("{:?}", config.fetch_path("placki.średnica").and_then(|val| val.as_int())); 14 | println!("{:?}", config.fetch_path("non.existent.path").and_then(|val| val.as_string())); 15 | } 16 | -------------------------------------------------------------------------------- /examples/include.conf: -------------------------------------------------------------------------------- 1 | .include "https://raw.githubusercontent.com/hauleth/ucl-rs/dc036c070e379bb6f985db846bb97532b9db038d/examples/test.conf" 2 | -------------------------------------------------------------------------------- /examples/load_file.rs: -------------------------------------------------------------------------------- 1 | extern crate ucl; 2 | 3 | fn main() { 4 | let parser = ucl::Parser::new(); 5 | 6 | let config = match parser.parse_file("examples/test.conf") { 7 | Ok(conf) => conf, 8 | Err(err) => panic!("{:?}", err) 9 | }; 10 | 11 | println!("{:?}", config.fetch("lol").and_then(|val| val.as_string())); 12 | println!("{:?}", config.fetch_path("placki.duze").and_then(|val| val.as_bool())); 13 | println!("{:?}", config.fetch_path("placki.średnica").and_then(|val| val.as_int())); 14 | println!("{:?}", config.fetch_path("non.existent.path").and_then(|val| val.as_string())); 15 | } 16 | -------------------------------------------------------------------------------- /examples/static_data.rs: -------------------------------------------------------------------------------- 1 | extern crate ucl; 2 | 3 | static DOC: &'static str = r#" 4 | param = value; 5 | section { 6 | flag = true; 7 | number = 10k; 8 | subsection { 9 | hosts = { 10 | host = "localhost"; 11 | port = 9000 12 | } 13 | hosts = { 14 | host = "remotehost" 15 | port = 9090 16 | } 17 | } 18 | } 19 | "#; 20 | 21 | fn main() { 22 | let parser = ucl::Parser::new(); 23 | let doc = parser.parse(DOC).unwrap(); 24 | 25 | println!("{:?}", doc); 26 | println!("{:?}", doc.fetch("param")); 27 | println!("{:?}", doc.fetch("param").unwrap().as_string()); 28 | } 29 | -------------------------------------------------------------------------------- /examples/test.conf: -------------------------------------------------------------------------------- 1 | lol = "lol"; 2 | 3 | placki { 4 | duze = true; 5 | średnica = 10kb 6 | } 7 | -------------------------------------------------------------------------------- /libucl-sys/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /libucl-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libucl-sys" 3 | version = "0.1.5" 4 | description = "Low-level bindings to libucl." 5 | authors = ["Łukasz Niemier "] 6 | build = "build.rs" 7 | license-file = "LICENSE" 8 | repository = "https://github.com/hauleth/libucl-sys" 9 | 10 | [lib] 11 | name = "libucl_sys" 12 | path = "lib.rs" 13 | 14 | [dependencies] 15 | libc = "*" 16 | bitflags = "*" 17 | curl-sys = "*" 18 | -------------------------------------------------------------------------------- /libucl-sys/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Łukasz Niemier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /libucl-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::env; 3 | use std::io::ErrorKind; 4 | use std::path::PathBuf; 5 | 6 | fn main() { 7 | let src = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap()); 8 | let dst = PathBuf::from(&env::var("OUT_DIR").unwrap()); 9 | 10 | let mut cmd = Command::new("./autogen.sh"); 11 | cmd.current_dir(&src.join("libucl")); 12 | 13 | run(&mut cmd, "autogen.sh"); 14 | 15 | let mut cmd = Command::new("./configure"); 16 | cmd 17 | .current_dir(&src.join("libucl")) 18 | .arg(&format!("--prefix={}", dst.display())); 19 | run(cmd.arg("--enable-urls") 20 | .arg("--enable-regex") 21 | .arg("--disable-shared") 22 | .arg("--disable-dependency-tracking") 23 | .arg("--with-pic"), "configure"); 24 | 25 | let mut cmd = Command::new("make"); 26 | cmd 27 | .current_dir(&src.join("libucl")); 28 | run(cmd.arg("install"), "make"); 29 | 30 | println!("cargo:rustc-link-lib=static=ucl"); 31 | println!("cargo:rustc-link-search=native={}", dst.join("lib").display()); 32 | } 33 | 34 | fn run(cmd: &mut Command, program: &str) { 35 | println!("running: {:?}", cmd); 36 | let status = match cmd.status() { 37 | Ok(status) => status, 38 | Err(ref e) if e.kind() == ErrorKind::NotFound => { 39 | fail(&format!("failed to execute command: {}\nis `{}` not installed?", 40 | e, program)); 41 | } 42 | Err(e) => fail(&format!("failed to execute command: {}", e)), 43 | }; 44 | if !status.success() { 45 | fail(&format!("command did not execute successfully, got: {}", status)); 46 | } 47 | } 48 | 49 | fn fail(s: &str) -> ! { 50 | panic!("\n{}\n\nbuild script failed, must exit now", s) 51 | } 52 | -------------------------------------------------------------------------------- /libucl-sys/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(raw_pointer_derive)] 3 | 4 | extern crate libc; 5 | #[macro_use] extern crate bitflags; 6 | extern crate curl_sys; 7 | 8 | use libc::{ 9 | c_char, 10 | c_double, 11 | c_int, 12 | c_uint, 13 | c_uchar, 14 | c_void, 15 | int64_t, 16 | size_t, 17 | uint16_t, 18 | uint32_t, 19 | }; 20 | 21 | #[repr(C)] 22 | #[derive(Clone, Copy)] 23 | pub enum ucl_error_t { 24 | UCL_EOK = 0, 25 | UCL_ESYNTAX, 26 | UCL_EIO, 27 | UCL_ESTATE, 28 | UCL_ENESTED, 29 | UCL_EMACRO, 30 | UCL_EINTERNAL, 31 | UCL_ESSL 32 | } 33 | 34 | #[repr(C)] 35 | #[derive(Clone, Copy)] 36 | pub enum ucl_type_t { 37 | UCL_OBJECT = 0, 38 | UCL_ARRAY, 39 | UCL_INT, 40 | UCL_FLOAT, 41 | UCL_STRING, 42 | UCL_BOOLEAN, 43 | UCL_TIME, 44 | UCL_USERDATA, 45 | UCL_NULL 46 | } 47 | 48 | #[repr(C)] 49 | #[derive(Clone, Copy)] 50 | pub enum ucl_emitter { 51 | UCL_EMIT_JSON = 0, 52 | UCL_EMIT_JSON_COMPACT, 53 | UCL_EMIT_CONFIG, 54 | UCL_EMIT_YAML 55 | } 56 | 57 | bitflags! { 58 | #[repr(C)] 59 | flags ucl_parser_flags_t: c_int { 60 | const UCL_PARSER_DEFAULT = 0x0, 61 | const UCL_PARSER_KEY_LOWERCASE = 0x1, 62 | const UCL_PARSER_ZEROCOPY = 0x2, 63 | const UCL_PARSER_NO_TIME = 0x4, 64 | const UCL_PARSER_NO_IMPLICIT_ARRAYS = 0x8 65 | } 66 | } 67 | 68 | bitflags! { 69 | #[repr(C)] 70 | flags ucl_string_flags_t : c_int { 71 | const UCL_STRING_RAW = 0x0, 72 | const UCL_STRING_ESCAPE = 0x1, 73 | const UCL_STRING_TRIM = 0x2, 74 | const UCL_STRING_PARSE_BOOLEAN = 0x4, 75 | const UCL_STRING_PARSE_INT = 0x8, 76 | const UCL_STRING_PARSE_DOUBLE = 0x10, 77 | const UCL_STRING_PARSE_TIME = 0x20, 78 | const UCL_STRING_PARSE_NUMBER = UCL_STRING_PARSE_INT.bits 79 | | UCL_STRING_PARSE_DOUBLE.bits 80 | | UCL_STRING_PARSE_TIME.bits, 81 | const UCL_STRING_PARSE = UCL_STRING_PARSE_BOOLEAN.bits 82 | | UCL_STRING_PARSE_NUMBER.bits, 83 | const UCL_STRING_PARSE_BYTES = 0x40 84 | } 85 | } 86 | 87 | bitflags! { 88 | #[repr(C)] 89 | flags ucl_object_flags_t: c_int { 90 | const UCL_OBJECT_ALLOCATED_KEY = 0x1, 91 | const UCL_OBJECT_ALLOCATED_VALUE = 0x2, 92 | const UCL_OBJECT_NEED_KEY_ESCAPE = 0x4, 93 | const UCL_OBJECT_EPHEMERAL = 0x8, 94 | const UCL_OBJECT_MULTILINE = 0x10, 95 | const UCL_OBJECT_MULTIVALUE = 0x20 96 | } 97 | } 98 | 99 | #[repr(C)] 100 | pub struct ucl_object_t { 101 | value: int64_t, 102 | pub key: *const c_char, 103 | pub next: *mut ucl_object_t, 104 | pub prev: *mut ucl_object_t, 105 | pub keylen: uint32_t, 106 | pub len: uint32_t, 107 | pub rc: uint32_t, 108 | pub flags: uint16_t, 109 | pub real_type: uint16_t, 110 | pub trash_stack: [*const c_char; 2] 111 | } 112 | 113 | impl ucl_object_t { 114 | pub unsafe fn iv(&self) -> int64_t { self.value } 115 | pub unsafe fn sv(&self) -> *const c_char { std::mem::transmute(self.value) } 116 | pub unsafe fn dv(&self) -> c_double { std::mem::transmute(self.value) } 117 | pub unsafe fn av(&self) -> *mut c_void { std::mem::transmute(self.value) } 118 | pub unsafe fn ov(&self) -> *mut c_void { std::mem::transmute(self.value) } 119 | pub unsafe fn uv(&self) -> *mut c_void { std::mem::transmute(self.value) } 120 | } 121 | 122 | pub type ucl_userdata_dtor = extern fn(*mut c_void); 123 | pub type ucl_userdata_emitter = extern fn(*mut c_void) -> *const c_char; 124 | pub type ucl_object_iter_t = *mut c_void; 125 | pub type ucl_macro_handler = extern fn(*const c_uchar, size_t, *const ucl_object_t, *mut c_void) -> bool; 126 | pub type ucl_variable_handler = extern fn(*const c_uchar, size_t, *mut *mut c_uchar, *mut size_t, *mut bool, *mut c_void) -> bool; 127 | 128 | #[repr(C)] 129 | pub struct ucl_parser; 130 | 131 | #[repr(C)] 132 | pub struct ucl_emitter_functions { 133 | ucl_emitter_append_character: extern fn(c_uchar, size_t, *mut c_void) -> c_int, 134 | ucl_emitter_append_len: extern fn(*const c_uchar, size_t, *mut c_void) -> c_int, 135 | ucl_emitter_append_int: extern fn(int64_t, *mut c_void) -> c_int, 136 | ucl_emitter_append_double: extern fn(c_double, *mut c_void) -> c_int, 137 | ucl_emitter_free_func: extern fn(*mut c_void), 138 | ud: *mut c_void 139 | } 140 | 141 | #[repr(C)] 142 | pub struct ucl_emitter_operations { 143 | ucl_emitter_write_elt: extern fn(*mut ucl_emitter_context, *const ucl_object_t, bool, bool), 144 | ucl_emitter_start_object: extern fn(*mut ucl_emitter_context, *const ucl_object_t, bool), 145 | ucl_emitter_end_object: extern fn(*mut ucl_emitter_context, *const ucl_object_t), 146 | ucl_emitter_start_array: extern fn(*mut ucl_emitter_context, *const ucl_object_t, bool), 147 | ucl_emitter_end_array: extern fn(*mut ucl_emitter_context, *const ucl_object_t), 148 | } 149 | 150 | #[repr(C)] 151 | pub struct ucl_emitter_context { 152 | name: *const c_char, 153 | id: c_int, 154 | func: *const ucl_emitter_functions, 155 | ops: *const ucl_emitter_operations, 156 | indent: c_uint, 157 | top: *const ucl_object_t, 158 | data: [c_uchar; 1] 159 | } 160 | 161 | #[repr(C)] 162 | #[derive(Clone, Copy)] 163 | pub enum ucl_schema_error_code { 164 | UCL_SCHEMA_OK = 0, 165 | UCL_SCHEMA_TYPE_MISMATCH, 166 | UCL_SCHEMA_INVALID_SCHEMA, 167 | UCL_SCHEMA_MISSING_PROPERTY, 168 | UCL_SCHEMA_CONSTRAINT, 169 | UCL_SCHEMA_MISSING_DEPENENCY, 170 | UCL_SCHEMA_UNKNOWN 171 | } 172 | 173 | #[repr(C)] 174 | pub struct ucl_schema_error { 175 | code: ucl_schema_error_code, 176 | msg: [c_char; 128], 177 | obj: *const ucl_object_t 178 | } 179 | 180 | extern { 181 | // Parser functions 182 | pub fn ucl_parser_new(flags: c_int) -> *mut ucl_parser; 183 | pub fn ucl_parser_register_macro(parser: *mut ucl_parser, macro_name: *const c_char, handler: ucl_macro_handler, ud: *mut c_void); 184 | pub fn ucl_parser_register_variable(parser: *mut ucl_parser, var: *const c_char, value: *const c_char); 185 | pub fn ucl_parser_add_chunk(parser: *mut ucl_parser, data: *const c_char, len: size_t) -> bool; 186 | pub fn ucl_parser_add_string(parser: *mut ucl_parser, data: *const c_char, len: size_t) -> bool; 187 | pub fn ucl_parser_add_file(parser: *mut ucl_parser, filename: *const c_char) -> bool; 188 | pub fn ucl_parser_get_object(parser: *mut ucl_parser) -> *mut ucl_object_t; 189 | pub fn ucl_parser_get_error(parser: *mut ucl_parser) -> *const c_char; 190 | pub fn ucl_parser_free(parser: *mut ucl_parser); 191 | pub fn ucl_parser_set_filevars(parser: *mut ucl_parser, filename: *const c_char, need_expand: bool) -> bool; 192 | pub fn ucl_parser_set_default_priority(parser: *mut ucl_parser, prio: c_uint) -> bool; 193 | pub fn ucl_parser_set_variables_handler(parser: *mut ucl_parser, handler: ucl_variable_handler, ud: *mut c_void); 194 | pub fn ucl_parser_add_chunk_priority(parser: *mut ucl_parser, data: *const c_uchar, len: size_t, prio: c_uint) -> bool; 195 | pub fn ucl_parser_add_string_priority(parser: *mut ucl_parser, data: *const c_uchar, len: size_t, prio: c_uint) -> bool; 196 | pub fn ucl_parser_add_file_priority(parser: *mut ucl_parser, filename: *const c_uchar, prio: c_uint) -> bool; 197 | pub fn ucl_parser_add_fd(parser: *mut ucl_parser, fd: c_int) -> bool; 198 | pub fn ucl_parser_add_fd_priority(parser: *mut ucl_parser, fd: c_int, prio: c_uint) -> bool; 199 | pub fn ucl_parser_clear_error(parser: *mut ucl_parser); 200 | pub fn ucl_parser_get_error_code(parser: *mut ucl_parser) -> c_int; 201 | pub fn ucl_parser_get_error_column(parser: *mut ucl_parser) -> c_uint; 202 | pub fn ucl_parser_get_error_linenum(parser: *mut ucl_parser) -> c_uint; 203 | 204 | // Pubkey 205 | pub fn ucl_pubkey_add(parser: *mut ucl_parser, key: *const c_char, len: size_t) -> bool; 206 | 207 | // Emit functions 208 | pub fn ucl_object_emit(obj: *const ucl_object_t, emit_type: ucl_emitter) -> *mut c_char; 209 | // pub fn ucl_object_emit_full(obj: *const ucl_object_t, emit_type: ucl_emitter, ) -> bool; 210 | // UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_memory_funcs ( 211 | // UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_file_funcs ( 212 | // UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_fd_funcs ( 213 | // UCL_EXTERN void ucl_object_emit_streamline_start_container ( 214 | // UCL_EXTERN void ucl_object_emit_streamline_add_object ( 215 | // UCL_EXTERN void ucl_object_emit_streamline_end_container ( 216 | // UCL_EXTERN void ucl_object_emit_streamline_finish ( 217 | pub fn ucl_object_emit_funcs_free(f: *mut ucl_emitter_functions); 218 | // UCL_EXTERN struct ucl_emitter_context* ucl_object_emit_streamline_new ( 219 | 220 | // Conversion functions 221 | pub fn ucl_object_toboolean(obj: *const ucl_object_t) -> bool; 222 | pub fn ucl_object_toboolean_safe(obj: *const ucl_object_t, target: *mut bool) -> bool; 223 | pub fn ucl_object_todouble(obj: *const ucl_object_t) -> c_double; 224 | pub fn ucl_object_todouble_safe (obj: *const ucl_object_t, target: *mut c_double) -> bool; 225 | pub fn ucl_object_toint(obj: *const ucl_object_t) -> int64_t; 226 | pub fn ucl_object_toint_safe(obj: *const ucl_object_t, target: *mut int64_t) -> bool; 227 | pub fn ucl_object_tolstring(obj: *const ucl_object_t) -> *const c_char; 228 | pub fn ucl_object_tostring(obj: *const ucl_object_t) -> *const c_char; 229 | pub fn ucl_object_tostring_forced(obj: *const ucl_object_t) -> *const c_char; 230 | pub fn ucl_object_tostring_safe(obj: *const ucl_object_t, target: *mut *const c_char) -> bool; 231 | pub fn ucl_object_tolstring_safe(obj: *const ucl_object_t, target: *mut *const c_char, len: *mut size_t) -> bool; 232 | 233 | // Generation functions 234 | pub fn ucl_object_new() -> *mut ucl_object_t; 235 | pub fn ucl_object_new_full(val: ucl_type_t, prio: c_uint) -> *mut ucl_object_t; 236 | pub fn ucl_object_typed_new(val: ucl_type_t) -> *mut ucl_object_t; 237 | pub fn ucl_object_new_userdata(dtor: ucl_userdata_dtor, emitter: ucl_userdata_emitter) -> *mut ucl_object_t; 238 | pub fn ucl_object_fromint(val: int64_t) -> *mut ucl_object_t; 239 | pub fn ucl_object_fromdouble(val: c_double) -> *mut ucl_object_t; 240 | pub fn ucl_object_frombool(val: bool) -> *mut ucl_object_t; 241 | pub fn ucl_object_fromstring(val: *const c_char) -> *mut ucl_object_t; 242 | pub fn ucl_object_fromlstring(val: *const c_char, len: size_t) -> *mut ucl_object_t; 243 | pub fn ucl_object_fromstring_common(val: *const c_char, len: size_t, flags: ucl_string_flags_t) -> *mut ucl_object_t; 244 | 245 | // Utility functions 246 | pub fn ucl_copy_key_trash(obj: *const ucl_object_t) -> *mut c_char; 247 | pub fn ucl_copy_value_trash(obj: *const ucl_object_t) -> *mut c_char; 248 | pub fn ucl_object_copy(other: *const ucl_object_t) -> *mut ucl_object_t; 249 | pub fn ucl_object_type(obj: *const ucl_object_t) -> ucl_type_t; 250 | 251 | // Object manipulation 252 | // UCL_EXTERN bool ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, 253 | // UCL_EXTERN bool ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, 254 | pub fn ucl_object_merge(top: *mut ucl_object_t, elt: *mut ucl_object_t, copy: bool) -> bool; 255 | // UCL_EXTERN bool ucl_object_delete_keyl (ucl_object_t *top, 256 | // UCL_EXTERN bool ucl_object_delete_key (ucl_object_t *top, 257 | // UCL_EXTERN ucl_object_t* ucl_object_pop_keyl (ucl_object_t *top, const char *key, 258 | pub fn ucl_object_pop_key(top: *mut ucl_object_t, key: *const c_char) -> *mut ucl_object_t; 259 | // UCL_EXTERN bool ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt, 260 | 261 | // Array manipulation 262 | // UCL_EXTERN bool ucl_array_append (ucl_object_t *top, 263 | // UCL_EXTERN bool ucl_array_prepend (ucl_object_t *top, 264 | // UCL_EXTERN bool ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, 265 | // UCL_EXTERN ucl_object_t* ucl_array_delete (ucl_object_t *top, 266 | pub fn ucl_array_head(top: *const ucl_object_t) -> *mut ucl_object_t; 267 | pub fn ucl_array_tail(top: *const ucl_object_t) -> *mut ucl_object_t; 268 | pub fn ucl_array_pop_last(top: *mut ucl_object_t) -> *mut ucl_object_t; 269 | pub fn ucl_array_pop_first(top: *mut ucl_object_t) -> *mut ucl_object_t; 270 | pub fn ucl_array_find_index(top: *const ucl_object_t, index: c_uint) -> *const ucl_object_t; 271 | // UCL_EXTERN unsigned int ucl_array_index_of (ucl_object_t *top, 272 | 273 | // Iteration functions 274 | pub fn ucl_iterate_object(obj: *const ucl_object_t, iter: *mut ucl_object_iter_t, expand_values: bool) -> *const ucl_object_t; 275 | pub fn ucl_object_iterate_new(obj: *const ucl_object_t) -> ucl_object_iter_t; 276 | pub fn ucl_object_iterate_reset(it: ucl_object_iter_t, obj: *const ucl_object_t) -> ucl_object_iter_t; 277 | pub fn ucl_object_iterate_safe(iter: ucl_object_iter_t, expand_values: bool) -> *const ucl_object_t; 278 | pub fn ucl_object_iterate_free(it: ucl_object_iter_t); 279 | 280 | // UCL_EXTERN ucl_object_t * ucl_elt_append (ucl_object_t *head, 281 | pub fn ucl_object_find_key(obj: *const ucl_object_t, key: *const c_char) -> *const ucl_object_t; 282 | // UCL_EXTERN const ucl_object_t* ucl_object_find_keyl (const ucl_object_t *obj, 283 | pub fn ucl_lookup_path(obj: *const ucl_object_t, path: *const c_char) -> *const ucl_object_t; 284 | // UCL_EXTERN const ucl_object_t *ucl_lookup_path_char (const ucl_object_t *obj, 285 | pub fn ucl_object_key (obj: *const ucl_object_t) -> *const c_char; 286 | pub fn ucl_object_keyl(obj: *const ucl_object_t, len: *mut size_t) -> *const c_char; 287 | pub fn ucl_object_ref(obj: *const ucl_object_t) -> *mut ucl_object_t; 288 | // UCL_DEPRECATED(UCL_EXTERN void ucl_object_free (ucl_object_t *obj)); 289 | pub fn ucl_object_unref(obj: *mut ucl_object_t); 290 | // UCL_EXTERN int ucl_object_compare (const ucl_object_t *o1, 291 | // UCL_EXTERN void ucl_object_array_sort (ucl_object_t *ar, 292 | pub fn ucl_object_get_priority(obj: *const ucl_object_t) -> c_uint; 293 | // UCL_EXTERN void ucl_object_set_priority (ucl_object_t *obj, 294 | // UCL_EXTERN bool ucl_object_validate (const ucl_object_t *schema, 295 | } 296 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use libucl_sys::ucl_error_t; 2 | 3 | #[derive(Clone, Debug)] 4 | pub enum Error { 5 | Ok, 6 | Syntax(String), 7 | Io, 8 | State, 9 | Nested, 10 | Macro, 11 | Internal, 12 | SSL, 13 | Other 14 | } 15 | 16 | impl Error { 17 | pub fn from_code(num: i32, desc: String) -> Self { 18 | match num { 19 | _ if num == ucl_error_t::UCL_EOK as i32 => Error::Ok, 20 | _ if num == ucl_error_t::UCL_ESYNTAX as i32 => Error::Syntax(desc), 21 | _ if num == ucl_error_t::UCL_EIO as i32 => Error::Io, 22 | _ if num == ucl_error_t::UCL_ESTATE as i32 => Error::State, 23 | _ if num == ucl_error_t::UCL_ENESTED as i32 => Error::Nested, 24 | _ if num == ucl_error_t::UCL_EMACRO as i32 => Error::Macro, 25 | _ if num == ucl_error_t::UCL_EINTERNAL as i32 => Error::Internal, 26 | _ if num == ucl_error_t::UCL_ESSL as i32 => Error::SSL, 27 | _ => Error::Other, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # UCL (Universal Configuration Library) 2 | //! 3 | //! This library is parser for UCL files. 4 | //! 5 | //! ## Basic structure 6 | //! 7 | //! UCL provide support for 2 different syntaxes: 8 | //! 9 | //! - JSON 10 | //! 11 | //! ```javascript 12 | //! { 13 | //! "param": "value", 14 | //! "section": { 15 | //! "flag": true, 16 | //! "number": 10000, 17 | //! "subsection": { 18 | //! "hosts": [ 19 | //! { 20 | //! "host": "localhost", 21 | //! "port": 9000 22 | //! }, 23 | //! { 24 | //! "host": "remotehost", 25 | //! "port": 9090 26 | //! } 27 | //! } 28 | //! } 29 | //! } 30 | //! ``` 31 | //! 32 | //! - nginx like UCL 33 | //! 34 | //! ```nginx 35 | //! param = value; 36 | //! section { 37 | //! flag = true; 38 | //! number = 10k; 39 | //! subsection { 40 | //! hosts = { 41 | //! host = "localhost"; 42 | //! port = 9000 43 | //! } 44 | //! hosts = { 45 | //! host = "remotehost" 46 | //! port = 9090 47 | //! } 48 | //! } 49 | //! } 50 | //! ``` 51 | //! 52 | //! Differences between UCL and JSON: 53 | //! 54 | //! - outmost braces are optional so `{"a": "b"}` is equivalent to `"a": "b"` 55 | //! - quotes on keys and strings are optional 56 | //! - `:` can be replaced with `=` or even skipped for objects 57 | //! - comma can be replaced with semicolon 58 | //! - trailing commas are allowed 59 | //! - automatic array creation - non-unique keys in object are allowed and are automatically 60 | //! converted to arrays 61 | //! 62 | //! ## Parser usage 63 | //! 64 | //! Simple example: 65 | //! 66 | //! ```rust 67 | //! static DOC: &'static str = r#" 68 | //! param = value; 69 | //! section { 70 | //! flag = true; 71 | //! number = 10k; 72 | //! subsection { 73 | //! hosts = { 74 | //! host = "localhost"; 75 | //! port = 9000 76 | //! } 77 | //! hosts = { 78 | //! host = "remotehost" 79 | //! port = 9090 80 | //! } 81 | //! } 82 | //! } 83 | //! "#; 84 | //! 85 | //! let parser = ucl::Parser::new(); 86 | //! let document = parser.parse(DOC).unwrap(); 87 | //! 88 | //! assert_eq!(document.fetch("param").unwrap().as_string(), Some("value".to_string())); 89 | //! ``` 90 | 91 | extern crate libucl_sys; 92 | extern crate libc; 93 | #[macro_use] extern crate bitflags; 94 | 95 | pub use error::Error; 96 | pub use parser::Parser; 97 | pub use object::Object; 98 | 99 | pub type Result = std::result::Result; 100 | 101 | mod utils; 102 | pub mod error; 103 | pub mod parser; 104 | pub mod object; 105 | -------------------------------------------------------------------------------- /src/object/builder.rs: -------------------------------------------------------------------------------- 1 | use libucl_sys::*; 2 | 3 | use utils; 4 | use super::Object; 5 | 6 | use std::convert::From; 7 | 8 | /// Build element object. 9 | /// 10 | /// This structure is immutable typed reference to object inside parsed tree. It can be one of 11 | /// `Type` elements and can be cast only to given type. 12 | pub struct Builder { 13 | obj: *mut ucl_object_t 14 | } 15 | 16 | impl Builder { 17 | /// Create new `Object` form raw pointer. Internal use only. 18 | pub fn from_ptr(obj: *mut ucl_object_t) -> Option { 19 | if !obj.is_null() { 20 | Some(Builder { 21 | obj: obj, 22 | }) 23 | } else { 24 | None 25 | } 26 | } 27 | 28 | pub fn build(self) -> Object { 29 | Object::from_cptr(self.obj).unwrap() 30 | } 31 | } 32 | 33 | impl Into for Builder { 34 | fn into(self) -> Object { 35 | self.build() 36 | } 37 | } 38 | 39 | macro_rules! from_primitive { 40 | ($from: ty => $ctype: ident, $func: ident) => { 41 | impl From<$from> for Builder { 42 | fn from(val: $from) -> Self { 43 | use libc; 44 | Builder::from_ptr(unsafe { $func(val as libc::$ctype) }).unwrap() 45 | } 46 | } 47 | }; 48 | 49 | ($from: ty, $func: ident) => { 50 | impl From<$from> for Builder { 51 | fn from(val: $from) -> Self { 52 | Builder::from_ptr(unsafe { $func(val) }).unwrap() 53 | } 54 | } 55 | } 56 | } 57 | 58 | from_primitive!(i64 => int64_t, ucl_object_fromint); 59 | from_primitive!(f64 => c_double, ucl_object_fromdouble); 60 | from_primitive!(bool, ucl_object_frombool); 61 | 62 | impl From for Builder { 63 | fn from(val: String) -> Self { 64 | use libc; 65 | use libucl_sys::ucl_object_fromlstring; 66 | 67 | let len = val.len(); 68 | Builder::from_ptr(unsafe { ucl_object_fromlstring(utils::to_c_str(val), len as libc::size_t) }).unwrap() 69 | } 70 | } 71 | 72 | impl<'a> From<&'a str> for Builder { 73 | fn from(val: &str) -> Self { 74 | From::from(val.to_string()) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/object/emitter.rs: -------------------------------------------------------------------------------- 1 | use libucl_sys::*; 2 | 3 | use super::Object; 4 | 5 | use utils; 6 | 7 | #[derive(Copy, Clone, PartialEq, Eq)] 8 | pub enum Emitter { 9 | JSON, 10 | JSONCompact, 11 | Config, 12 | YAML 13 | } 14 | 15 | impl Emitter { 16 | pub fn emit>(&self, obj: T) -> Option { 17 | let emit = unsafe { ucl_object_emit(obj.as_ref().obj, Into::into(*self)) }; 18 | utils::to_str(emit) 19 | } 20 | } 21 | 22 | impl From for Emitter { 23 | fn from(raw: ucl_emitter) -> Self { 24 | match raw { 25 | ucl_emitter::UCL_EMIT_JSON => Emitter::JSON, 26 | ucl_emitter::UCL_EMIT_JSON_COMPACT => Emitter::JSONCompact, 27 | ucl_emitter::UCL_EMIT_CONFIG => Emitter::Config, 28 | ucl_emitter::UCL_EMIT_YAML => Emitter::YAML, 29 | } 30 | } 31 | } 32 | 33 | impl Into for Emitter { 34 | fn into(self) -> ucl_emitter { 35 | match self { 36 | Emitter::JSON => ucl_emitter::UCL_EMIT_JSON, 37 | Emitter::JSONCompact => ucl_emitter::UCL_EMIT_JSON_COMPACT, 38 | Emitter::Config => ucl_emitter::UCL_EMIT_CONFIG, 39 | Emitter::YAML => ucl_emitter::UCL_EMIT_YAML 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/object/mod.rs: -------------------------------------------------------------------------------- 1 | use libucl_sys::*; 2 | 3 | pub use self::types::Type; 4 | pub use self::builder::Builder; 5 | pub use self::emitter::Emitter; 6 | use utils; 7 | 8 | use std::convert::From; 9 | use std::fmt; 10 | 11 | pub mod types; 12 | pub mod builder; 13 | pub mod emitter; 14 | 15 | #[cfg(test)] 16 | mod test; 17 | 18 | /// File element object. 19 | /// 20 | /// This structure is immutable typed reference to object inside parsed tree. It can be one of 21 | /// `Type` elements and can be cast only to given type. 22 | pub struct Object { 23 | obj: *const ucl_object_t, 24 | typ: Type 25 | } 26 | 27 | impl Object { 28 | /// Create new `Object` form const raw pointer. Internal use only. 29 | fn from_cptr(obj: *const ucl_object_t) -> Option { 30 | if !obj.is_null() { 31 | Some(Object { 32 | obj: obj, 33 | typ: Type::from(unsafe { ucl_object_type(obj) }) 34 | }) 35 | } else { 36 | None 37 | } 38 | } 39 | 40 | // pub fn priority(&self) -> usize { 41 | // unsafe { ucl_object_get_priority(self.obj) as usize } 42 | // } 43 | 44 | /// Return key assigned to object. 45 | pub fn key(&self) -> Option { 46 | utils::to_str(unsafe { ucl_object_key(self.obj) }) 47 | } 48 | 49 | /// Return type of object. 50 | pub fn get_type(&self) -> Type { 51 | self.typ 52 | } 53 | 54 | /// Return `i64` value 55 | /// 56 | /// # Examples 57 | /// 58 | /// ```rust 59 | /// let obj = ucl::object::Builder::from(10).build(); 60 | /// assert_eq!(obj.as_int(), Some(10)); 61 | /// 62 | /// let obj = ucl::object::Builder::from("lol").build(); 63 | /// assert_eq!(obj.as_int(), None); 64 | /// ``` 65 | pub fn as_int(&self) -> Option { 66 | use libucl_sys::ucl_object_toint_safe; 67 | 68 | if self.get_type() != Type::Int { return None } 69 | 70 | unsafe { 71 | let out: *mut i64 = &mut 0i64; 72 | let res = ucl_object_toint_safe(self.obj, out); 73 | 74 | if res && !out.is_null() { 75 | Some(*out) 76 | } else { 77 | None 78 | } 79 | } 80 | } 81 | 82 | /// Return `f64` value 83 | /// 84 | /// # Examples 85 | /// 86 | /// ```rust 87 | /// let obj = ucl::object::Builder::from(10f64).build(); 88 | /// assert_eq!(obj.as_float(), Some(10.0)); 89 | /// 90 | /// let obj = ucl::object::Builder::from("lol").build(); 91 | /// assert_eq!(obj.as_float(), None); 92 | /// ``` 93 | pub fn as_float(&self) -> Option { 94 | use libucl_sys::ucl_object_todouble_safe; 95 | 96 | if self.get_type() != Type::Float { return None } 97 | 98 | unsafe { 99 | let out: *mut f64 = &mut 0f64; 100 | let res = ucl_object_todouble_safe(self.obj, out); 101 | 102 | if res && !out.is_null() { 103 | Some(*out) 104 | } else { 105 | None 106 | } 107 | } 108 | } 109 | 110 | /// Return boolean value 111 | /// 112 | /// # Examples 113 | /// 114 | /// ```rust 115 | /// let obj = ucl::object::Builder::from(true).build(); 116 | /// assert_eq!(obj.as_bool(), Some(true)); 117 | /// 118 | /// let obj = ucl::object::Builder::from(10).build(); 119 | /// assert_eq!(obj.as_bool(), None); 120 | /// ``` 121 | pub fn as_bool(&self) -> Option { 122 | use libucl_sys::ucl_object_toboolean_safe; 123 | 124 | if self.get_type() != Type::Boolean { return None } 125 | 126 | unsafe { 127 | let out: *mut bool = &mut true; 128 | let res = ucl_object_toboolean_safe(self.obj, out); 129 | 130 | if res && !out.is_null() { 131 | Some(*out) 132 | } else { 133 | None 134 | } 135 | } 136 | } 137 | 138 | /// Return string value 139 | /// 140 | /// # Examples 141 | /// 142 | /// ```rust 143 | /// let obj = ucl::object::Builder::from("lol").build(); 144 | /// assert_eq!(obj.as_string(), Some("lol".to_string())); 145 | /// 146 | /// let obj = ucl::object::Builder::from(10).build(); 147 | /// assert_eq!(obj.as_string(), None); 148 | /// ``` 149 | pub fn as_string(&self) -> Option { 150 | use libucl_sys::ucl_object_tostring; 151 | 152 | if self.get_type() != Type::String { return None } 153 | 154 | unsafe { 155 | let out = ucl_object_tostring(self.obj); 156 | 157 | utils::to_str(out) 158 | } 159 | } 160 | 161 | /// Fetch object under key 162 | /// 163 | /// # Examples 164 | /// 165 | /// ```rust 166 | /// let obj = ucl::Parser::new().parse("a = b;").unwrap(); 167 | /// assert_eq!(obj.fetch("a").unwrap().as_string(), Some("b".to_string())); 168 | /// ``` 169 | pub fn fetch>(&self, key: T) -> Option { 170 | use libucl_sys::ucl_object_find_key; 171 | 172 | if self.get_type() != Type::Object { return None } 173 | 174 | unsafe { 175 | let out = ucl_object_find_key(self.obj, utils::to_c_str(key.as_ref())); 176 | 177 | Object::from_cptr(out) 178 | } 179 | } 180 | 181 | /// Fetch object at the end of path delimeted by `.` (dot) 182 | /// 183 | /// # Examples 184 | /// 185 | /// ```rust 186 | /// let obj = ucl::Parser::new().parse("a = { b = c; }").unwrap(); 187 | /// assert_eq!(obj.fetch_path("a.b").unwrap().as_string(), Some("c".to_string())); 188 | /// ``` 189 | pub fn fetch_path>(&self, path: T) -> Option { 190 | use libucl_sys::ucl_lookup_path; 191 | 192 | if self.get_type() != Type::Object { return None } 193 | 194 | unsafe { 195 | let out = ucl_lookup_path(self.obj, utils::to_c_str(path.as_ref())); 196 | 197 | Object::from_cptr(out) 198 | } 199 | } 200 | } 201 | 202 | impl AsRef for Object { 203 | fn as_ref(&self) -> &Self { self } 204 | } 205 | 206 | impl fmt::Debug for Object { 207 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 208 | let string = Emitter::JSON.emit(&self); 209 | 210 | if string.is_some() { 211 | fmt.write_str(&string.unwrap()) 212 | } else { 213 | Err(fmt::Error) 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/object/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn from_int() { 5 | let obj = Builder::from(10).build(); 6 | assert_eq!(obj.get_type(), Type::Int); 7 | } 8 | 9 | #[test] 10 | fn from_double() { 11 | let obj = Builder::from(10.0f64).build(); 12 | assert_eq!(obj.get_type(), Type::Float); 13 | } 14 | 15 | #[test] 16 | fn from_bool() { 17 | let obj = Builder::from(true).build(); 18 | assert_eq!(obj.get_type(), Type::Boolean); 19 | } 20 | 21 | #[test] 22 | fn from_string() { 23 | let obj = Builder::from("lol".to_string()).build(); 24 | assert_eq!(obj.get_type(), Type::String); 25 | } 26 | 27 | #[test] 28 | fn from_str() { 29 | let obj = Builder::from("lol").build(); 30 | assert_eq!(obj.get_type(), Type::String); 31 | } 32 | 33 | #[test] 34 | fn to_int() { 35 | let obj = Builder::from(10).build(); 36 | assert_eq!(obj.as_int(), Some(10)); 37 | } 38 | 39 | #[test] 40 | fn to_string() { 41 | let obj = Builder::from("lol").build(); 42 | assert_eq!(obj.as_string(), Some("lol".to_string())); 43 | } 44 | 45 | #[test] 46 | fn to_int_invalid_type() { 47 | let obj = Builder::from(10.0f64).build(); 48 | assert_eq!(obj.as_int(), None); 49 | } 50 | -------------------------------------------------------------------------------- /src/object/types.rs: -------------------------------------------------------------------------------- 1 | use libucl_sys::ucl_type_t; 2 | 3 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 4 | pub enum Type { 5 | Object, 6 | Array, 7 | Int, 8 | Float, 9 | String, 10 | Boolean, 11 | Time, 12 | UserData, 13 | Null 14 | } 15 | 16 | impl From for Type { 17 | fn from(typ: ucl_type_t) -> Self { 18 | match typ { 19 | ucl_type_t::UCL_OBJECT => Type::Object, 20 | ucl_type_t::UCL_ARRAY => Type::Array, 21 | ucl_type_t::UCL_INT => Type::Int, 22 | ucl_type_t::UCL_FLOAT => Type::Float, 23 | ucl_type_t::UCL_STRING => Type::String, 24 | ucl_type_t::UCL_BOOLEAN => Type::Boolean, 25 | ucl_type_t::UCL_TIME => Type::Time, 26 | ucl_type_t::UCL_USERDATA => Type::UserData, 27 | ucl_type_t::UCL_NULL => Type::Null, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use libucl_sys::*; 2 | use libc::size_t; 3 | 4 | use utils; 5 | use error; 6 | use super::Result; 7 | use object::{ 8 | self, 9 | Object 10 | }; 11 | 12 | use std::path::Path; 13 | 14 | bitflags! { 15 | flags Flags: i32 { 16 | const DEFAULT = 0x0, 17 | const LOWERCASE = 0x1, 18 | const ZEROCOPY = 0x2, 19 | const NO_TIME = 0x4, 20 | const NO_IMPLICIT_ARRAYS = 0x8 21 | } 22 | } 23 | 24 | pub struct Parser { 25 | parser: *mut ucl_parser, 26 | } 27 | 28 | impl Parser { 29 | /// Create new parser instance with default options 30 | pub fn new() -> Self { 31 | Self::with_flags(DEFAULT) 32 | } 33 | 34 | /// Create new parser with given option flags 35 | /// 36 | /// Flags: 37 | /// 38 | /// - `DEFAULT` - default configuration 39 | /// - `LOWERCASE` - convert all keys to lower case 40 | /// - `ZEROCOPY` - parse input in zero-copy mode if possible (you must ensure that input memory 41 | /// is not freed if an object is in use) 42 | /// - `NO_TIME` - do not parse time and treat it's value as string 43 | /// - `NO_IMPLICIT_ARRAYS` - create explicit arrays instead of implicit ones 44 | /// 45 | /// # Examples 46 | /// 47 | /// ```rust 48 | /// let parser = ucl::Parser::with_flags(ucl::parser::LOWERCASE); 49 | /// let doc = parser.parse("A = b").unwrap(); 50 | /// 51 | /// assert!(doc.fetch("a").is_some()); 52 | /// ``` 53 | pub fn with_flags(flags: Flags) -> Self { 54 | Parser { 55 | parser: unsafe { ucl_parser_new(flags.bits()) } 56 | } 57 | } 58 | 59 | /// Parse given string. Returns root object on success. 60 | /// 61 | /// It moves out `Parser`. 62 | /// 63 | /// # Examples 64 | /// 65 | /// ```rust 66 | /// assert!(ucl::Parser::new().parse("a = b").is_ok()); 67 | /// assert!(ucl::Parser::new().parse("a =").is_err()); 68 | /// ``` 69 | pub fn parse>(mut self, string: T) -> Result { 70 | let len = string.as_ref().len() as size_t; 71 | let result = unsafe { ucl_parser_add_chunk(self.parser, utils::to_c_str(string), len) }; 72 | 73 | if result { 74 | Ok(self.get_object().unwrap()) 75 | } else { 76 | Err(self.get_error()) 77 | } 78 | } 79 | 80 | /// Parse file at given `Path`. 81 | /// 82 | /// It moves out `Parser`. 83 | pub fn parse_file>(mut self, path: T) -> Result { 84 | let filename = path.as_ref().to_str().unwrap(); 85 | let result = unsafe { ucl_parser_add_file(self.parser, utils::to_c_str(filename)) }; 86 | 87 | if result { 88 | Ok(self.get_object().unwrap()) 89 | } else { 90 | Err(self.get_error()) 91 | } 92 | } 93 | 94 | /// Register new variable 95 | /// 96 | /// # Examples 97 | /// 98 | /// ```rust 99 | /// let p = ucl::Parser::new(); 100 | /// p.register_var("LOL".to_string(), "test".to_string()); 101 | /// let res = p.parse("lol = $LOL").unwrap(); 102 | /// 103 | /// assert_eq!(res.fetch("lol").unwrap().as_string(), Some("test".to_string())); 104 | /// ``` 105 | pub fn register_var(&self, name: String, value: String) { 106 | unsafe { 107 | ucl_parser_register_variable(self.parser, utils::to_c_str(name), utils::to_c_str(value)) 108 | } 109 | } 110 | 111 | fn get_object(&mut self) -> Option { 112 | object::Builder::from_ptr(unsafe { ucl_parser_get_object(self.parser) }).map(|o| o.build()) 113 | } 114 | 115 | fn get_error(&mut self) -> error::Error { 116 | let err = unsafe { ucl_parser_get_error_code(self.parser) }; 117 | let desc = unsafe { ucl_parser_get_error(self.parser) }; 118 | 119 | error::Error::from_code(err, utils::to_str(desc).unwrap()) 120 | } 121 | } 122 | 123 | impl Drop for Parser { 124 | fn drop(&mut self) { 125 | unsafe { ucl_parser_free(self.parser) } 126 | } 127 | } 128 | 129 | #[cfg(test)] 130 | mod test { 131 | use super::*; 132 | 133 | #[test] 134 | fn string_parsing() { 135 | let p = Parser::new(); 136 | let s = r#"lol = "lol""#; 137 | 138 | assert!(p.parse(s).is_ok()); 139 | } 140 | 141 | #[test] 142 | fn empty_string_parsing() { 143 | let p = Parser::new(); 144 | let s = r#""#; 145 | 146 | assert!(p.parse(s).is_ok()); 147 | } 148 | 149 | #[test] 150 | fn key_fetching() { 151 | let p = Parser::new(); 152 | let s = r#"lol = 10"#; 153 | let res = p.parse(s).unwrap(); 154 | 155 | assert_eq!(res.fetch("lol").unwrap().as_int(), Some(10)); 156 | } 157 | 158 | #[test] 159 | fn flags() { 160 | let s = r#"LoL = 10"#; 161 | let p = Parser::with_flags(DEFAULT); 162 | let res = p.parse(s).unwrap(); 163 | 164 | assert!(res.fetch("lol").is_none()); 165 | 166 | let p = Parser::with_flags(LOWERCASE); 167 | let res = p.parse(s).unwrap(); 168 | 169 | assert_eq!(res.fetch("lol").unwrap().as_int(), Some(10)); 170 | } 171 | 172 | #[test] 173 | fn variables() { 174 | let s = r#"lol = $LOL"#; 175 | let p = Parser::new(); 176 | p.register_var("LOL".to_string(), "test".to_string()); 177 | let res = p.parse(s).unwrap(); 178 | 179 | assert_eq!(res.fetch("lol").unwrap().as_string(), Some("test".to_string())); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use libc::c_char; 2 | 3 | use std::str; 4 | use std::ffi::{ 5 | CString, 6 | CStr 7 | }; 8 | 9 | pub fn to_c_str>(string: T) -> *const c_char { 10 | CString::new(string.as_ref()).unwrap().as_ptr() 11 | } 12 | 13 | pub fn to_str(cstring: *const c_char) -> Option { 14 | if cstring.is_null() { return None } 15 | Some(str::from_utf8(unsafe { CStr::from_ptr(cstring).to_bytes() }).unwrap().to_string()) 16 | } 17 | --------------------------------------------------------------------------------