From 5d7bdfd7be77000e443b6003fd4f2b94bf663bae Mon Sep 17 00:00:00 2001 From: lurchi Date: Wed, 14 Sep 2016 00:04:40 +0200 Subject: [PATCH] added psyctext --- rust/src/lib.rs | 4 ++ rust/src/text.rs | 124 ++++++++++++++++++++++++++++++++++++++++ rust/src/text_types.rs | 41 +++++++++++++ rust/src/types.rs | 7 +++ rust/tests/test_text.rs | 62 ++++++++++++++++++++ 5 files changed, 238 insertions(+) create mode 100644 rust/src/text.rs create mode 100644 rust/src/text_types.rs create mode 100644 rust/tests/test_text.rs diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f63a058..6625892 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -6,6 +6,7 @@ mod keyword; mod method_types; mod packet_types; mod parser_types; +mod text_types; mod types; mod uniform_types; mod util; @@ -14,6 +15,7 @@ pub mod method; pub mod parser; pub mod packet; pub mod packet_id; +pub mod text; pub mod uniform; pub mod variable; @@ -26,6 +28,8 @@ pub use parser_types::{PsycParserResult, PsycListParserResult, PsycParserError, pub use packet::{PsycList, PsycModifier, PsycPacket}; pub use packet_id::PacketId; pub use packet_types::PacketRenderError; +pub use text::Text; +pub use text_types::SubstitutionResult; pub use uniform::Uniform; pub use uniform_types::{UniformParseError, PsycScheme, PsycEntity}; pub use packet_types::{PsycOperator, PsycStateOp}; diff --git a/rust/src/text.rs b/rust/src/text.rs new file mode 100644 index 0000000..9349d96 --- /dev/null +++ b/rust/src/text.rs @@ -0,0 +1,124 @@ +use text_types::*; +use types::PsycString; +use method_types::PsycMethod; +use util; +use std::os::raw::c_char; +use std::os::raw::c_void; +use std::ptr; +use std::mem; +use std::marker::PhantomData; + +extern "C" { + fn psyc_text_state_init(state: *mut PsycTextState, + tmpl: *const c_char, + tmplen: usize, + buffer: *mut c_char, + buflen: usize); + + fn psyc_text_state_init_custom(state: *mut PsycTextState, + tmpl_: *const c_char, + tmplen: usize, + buffer: *mut c_char, + buflen: usize, + ope: *const c_char, + opelen: usize, + clo: *const c_char, + clolen: usize); + + fn psyc_text_buffer_set(state: *mut PsycTextState, + buffer: *mut c_char, + length: usize); + + fn psyc_text_bytes_written(state: *const PsycTextState) -> usize; + + fn psyc_text(state: *mut PsycTextState, + get_value: extern fn(*const c_void, + *const c_char, + usize, + *mut PsycString) + -> PsycTextValueRC, + get_value_cls: *const c_void) + -> PsycTextRC; + + fn psyc_template(mc: PsycMethod, len: *mut usize) -> *const c_char; +} + +extern "C" fn callback(cls: *const c_void, + name: *const c_char, + namelen: usize, + value: *mut PsycString) + -> PsycTextValueRC { + let get_value: &&for<'a> Fn(&'a [u8]) -> Option<&[u8]> = unsafe { + mem::transmute(cls) + }; + let name_slice = unsafe { + util::cstring_to_slice(name, namelen) + }; + match get_value(name_slice) { + None => PsycTextValueRC::PSYC_TEXT_VALUE_NOT_FOUND, + Some(val) => { + let mut v: &mut PsycString = unsafe {&mut *value}; + v.data = val.as_ptr() as *const c_char; + v.length = val.len(); + PsycTextValueRC::PSYC_TEXT_VALUE_FOUND + } + } +} + +pub struct Text<'a> { + state: PsycTextState, + phantom_template: PhantomData<&'a Vec>, +} + +impl<'a> Text<'a> { + pub fn new(template: &'a [u8]) -> Self { + let mut state: PsycTextState; + let template_ptr = template.as_ptr() as *const c_char; + unsafe { + state = mem::uninitialized(); + let state_ptr = &mut state as *mut PsycTextState; + psyc_text_state_init(state_ptr, + template_ptr, + template.len(), + ptr::null_mut(), + 0); + } + Text { + state: state, + phantom_template: PhantomData, + } + } + + pub fn substitute_variables<'b, F>(&mut self, + get_value: F, + buffer: &'b mut [u8]) + -> SubstitutionResult + where F: Fn(&'a [u8]) -> Option<&[u8]> { + let state_ptr = &mut self.state as *mut PsycTextState; + let buffer_ptr = buffer.as_mut_ptr() as *mut c_char; + let trait_obj: &Fn(&'a [u8]) -> Option<&[u8]> = &get_value; + let trait_obj_ptr: *const c_void = unsafe {mem::transmute(&trait_obj)}; + unsafe { + psyc_text_buffer_set(state_ptr, buffer_ptr, buffer.len()); + let result = psyc_text(state_ptr, callback, trait_obj_ptr); + match result { + PsycTextRC::PSYC_TEXT_NO_SUBST => SubstitutionResult::NoSubstitution, + PsycTextRC::PSYC_TEXT_COMPLETE => SubstitutionResult::Complete { + bytes_written: psyc_text_bytes_written(state_ptr) + }, + PsycTextRC::PSYC_TEXT_INCOMPLETE => SubstitutionResult::Incomplete { + bytes_written: psyc_text_bytes_written(state_ptr) + } + } + } + } + + pub fn template(method: PsycMethod) -> &'static [u8] { + let mut result: PsycString; + unsafe { + result = mem::uninitialized(); + result.data = psyc_template(method, &mut result.length as *mut usize); + util::cstring_to_slice(result.data, result.length) + } + } +} diff --git a/rust/src/text_types.rs b/rust/src/text_types.rs new file mode 100644 index 0000000..894c0b9 --- /dev/null +++ b/rust/src/text_types.rs @@ -0,0 +1,41 @@ +use types::PsycString; + +#[repr(C)] +pub enum PsycTextRC { + /// No substitution was made, nothing was written to the buffer. + PSYC_TEXT_NO_SUBST = -1, + /// Text template parsing & rendering complete. + PSYC_TEXT_COMPLETE = 0, + /// Text template parsing & rendering is incomplete, because the buffer was too + /// small. Another call is required to this function after setting a new buffer. + PSYC_TEXT_INCOMPLETE = 1, +} + +#[repr(C)] +pub enum PsycTextValueRC { + /// Value not found, don't substitute anything. + PSYC_TEXT_VALUE_NOT_FOUND = -1, + /// Value found, substitute contents of the value variable. + PSYC_TEXT_VALUE_FOUND = 0, +} + +#[repr(C)] +pub struct PsycTextState { + cursor: usize, ///< current position in the template + written: usize, ///< number of bytes written to buffer + tmpl: PsycString, ///< input buffer with text template to parse + buffer: PsycString, ///< output buffer for rendered text + open: PsycString, + close: PsycString, +} + +#[derive(Debug, PartialEq)] +pub enum SubstitutionResult { + NoSubstitution, + Complete { + bytes_written: usize + }, + Incomplete { + bytes_written: usize + } +} diff --git a/rust/src/types.rs b/rust/src/types.rs index b23cfa3..e625c0f 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -20,6 +20,13 @@ pub struct PsycString { pub data: *const c_char } +//#[derive(Debug)] +//#[repr(C)] +//pub struct MutablePsycString { +// pub length: usize, +// pub data: *mut c_char +//} + impl PsycBool { pub fn to_bool(self) -> bool { match self { diff --git a/rust/tests/test_text.rs b/rust/tests/test_text.rs new file mode 100644 index 0000000..2995945 --- /dev/null +++ b/rust/tests/test_text.rs @@ -0,0 +1,62 @@ +extern crate psyc; + +use psyc::*; + +#[test] +fn test_substitute() { + let template = b"Your hello is [_hello]"; + let mut t = Text::new(template); + + let mut buffer = vec![0; 100]; + + let result = t.substitute_variables(|name| { + println!("in callback, name: {:?}", name); + match name { + b"_hello" => Some(b"hi"), + _ => None + }}, + &mut buffer); + + assert_eq!(result, SubstitutionResult::Complete {bytes_written: 16}); + assert_eq!(&buffer[.. 16], b"Your hello is hi"); +} + +#[test] +fn test_empty() { + let mut t = Text::new(b""); + + let mut buffer = Vec::new(); + + assert_eq!(t.substitute_variables(|_name| {Some(b"hi")}, &mut buffer), + SubstitutionResult::NoSubstitution); +} + +#[test] +fn test_incomplete() { + let template = b"Your hello is [_hello]"; + let mut t = Text::new(template); + + let mut buffer = vec![0; 10]; + + assert_eq!(t.substitute_variables(|_name| {Some(b"hi")}, &mut buffer), + SubstitutionResult::Incomplete {bytes_written: 0}); + + buffer.resize(15, 0); + + assert_eq!(t.substitute_variables(|_name| {Some(b"hi")}, &mut buffer), + SubstitutionResult::Incomplete {bytes_written: 14}); + + buffer.resize(16, 0); + assert_eq!(t.substitute_variables(|_name| {Some(b"hi")}, &mut buffer[14 ..]), + SubstitutionResult::Complete {bytes_written: 2}); + + assert_eq!(&buffer, b"Your hello is hi"); +} + +#[test] +fn test_template() { + let template = Text::template(PsycMethod::PSYC_MC_REQUEST_CONTEXT_ENTER); + let expected: &'static [u8] = b"[_source] asks for your permission to enter [_context]"; + + assert_eq!(template, expected); +}