added psyctext

This commit is contained in:
lurchi 2016-09-14 00:04:40 +02:00
parent 4de756b5fd
commit 5d7bdfd7be
5 changed files with 238 additions and 0 deletions

View File

@ -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};

124
rust/src/text.rs Normal file
View File

@ -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<u8>>,
}
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)
}
}
}

41
rust/src/text_types.rs Normal file
View File

@ -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
}
}

View File

@ -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 {

62
rust/tests/test_text.rs Normal file
View File

@ -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);
}