1
0
Fork 0
mirror of git://git.psyc.eu/libpsyc synced 2024-08-15 03:19:02 +00:00

first version of the rust bindings: parsing and rendering is mostly functional

This commit is contained in:
lurchi 2016-08-23 00:04:57 +02:00
parent 9d7ad2a67f
commit dd62ec934c
10 changed files with 1208 additions and 0 deletions

6
rust/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "psyc"
version = "1.0.0"
links = "psyc"
build = "build.rs"
description = "rust bindings for libpsyc"

18
rust/build.rs Normal file
View file

@ -0,0 +1,18 @@
use std::env;
use std::process::Command;
use std::path::*;
fn main() {
Command::new("make")
.arg("-C")
.arg("..")
.output()
.unwrap_or_else(|e| panic!("failed to execute process: {}", e));
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let path = Path::new(&manifest_dir).parent().unwrap().join("lib");
println!("cargo:rustc-link-search=native={}", path.display());
println!("cargo:rerun-if-changed={}", path.display());
println!("cargo:rustc-link-lib=static=psyc");
}

9
rust/src/lib.rs Normal file
View file

@ -0,0 +1,9 @@
#![allow(dead_code)]
mod types;
pub mod parser_types;
pub mod packet_types;
pub mod parser;
pub mod packet;
pub use parser::*;
pub use packet::*;

347
rust/src/packet.rs Normal file
View file

@ -0,0 +1,347 @@
use types::*;
use packet_types::*;
use std::mem;
use std::ptr;
use std::os::raw::c_char;
extern "C" {
/// functions from packet.h
fn psyc_num_length(n: usize) -> usize;
fn psyc_modifier_length_check(m: *const RawPsycModifier) -> PsycModifierFlag;
fn psyc_modifier_init(m: *mut RawPsycModifier,
oper: PsycOperator,
name: *const c_char,
namelen: usize,
value: *const c_char,
valuelen: usize,
flag: PsycModifierFlag);
fn psyc_elem_length_check(value: *const PsycString, end: c_char) -> PsycElemFlag;
fn psyc_elem_length(elem: *const PsycElem) -> usize;
fn psyc_dict_key_length(elem: *const PsycDictKey) -> usize;
fn psyc_list_length_set(list: *mut RawPsycList) -> usize;
fn psyc_dict_length_set(dict: *mut RawPsycDict) -> usize;
fn psyc_modifier_length(m: *const RawPsycModifier) -> usize;
fn psyc_packet_length_check(p: *const RawPsycPacket) -> PsycPacketFlag;
fn psyc_packet_length_set(p: *mut RawPsycPacket) -> usize;
fn psyc_list_init(list: *mut RawPsycList, elems: *const PsycElem, num_elems: usize);
fn psyc_dict_init(dict: *mut RawPsycDict, elems: *const PsycDictElem, num_elems: usize);
fn psyc_packet_init(packet: *mut RawPsycPacket,
routing: *const RawPsycModifier,
routinglen: usize,
entity: *const RawPsycModifier,
entitylen: usize,
method: *const c_char,
methodlen: usize,
data: *const c_char,
datalen: usize,
stateop: c_char,
flag: PsycPacketFlag);
fn psyc_packet_init_raw(packet: *mut RawPsycPacket,
routing: *const RawPsycModifier,
routinglen: usize,
content: *const c_char,
contentlen: usize,
flag: PsycPacketFlag);
fn psyc_packet_id(list: *mut RawPsycList,
elems: *mut PsycElem,
context: *const c_char,
contextlen: usize,
source: *const c_char,
sourcelen: usize,
target: *const c_char,
targetlen: usize,
counter: *const c_char,
counterlen: usize,
fragment: *const c_char,
fragmentlen: usize);
/// functions from render.h
fn psyc_render(packet: *const RawPsycPacket, buffer: *mut c_char, buflen: usize) -> PsycRenderRC;
fn psyc_render_modifier(modifier: *const RawPsycModifier, buffer: *mut c_char) -> usize;
fn psyc_render_elem(elem: *const PsycElem, buffer: *mut c_char, buflen: usize) -> PsycRenderRC;
fn psyc_render_dict_key(elem: *const PsycDictKey, buffer: *mut c_char, buflen: usize) -> PsycRenderRC;
fn psyc_render_list(list: *const RawPsycList, buffer: *mut c_char, buflen: usize) -> PsycRenderRC;
fn psyc_render_dict(dict: *const RawPsycDict, buffer: *mut c_char, buflen: usize) -> PsycRenderRC;
}
pub struct PsycList {
rendered_list: Vec<u8>
}
pub struct PsycDict {
rendered_dict: Vec<u8>
}
pub struct PsycModifier<'a> {
name: &'a str,
value: &'a [u8],
operator: PsycOperator,
}
pub struct PsycPacket<'a> {
raw_packet: RawPsycPacket,
routing_modifiers: &'a [PsycModifier<'a>],
entity_modifiers: &'a [PsycModifier<'a>],
raw_routing_modifiers: Vec<RawPsycModifier>,
raw_entity_modifiers: Vec<RawPsycModifier>,
method: &'a str,
body: &'a [u8]
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycRenderError {
MethodMissing = PsycRenderRC::PSYC_RENDER_ERROR_METHOD_MISSING as _,
ModifierNameMissing = PsycRenderRC::PSYC_RENDER_ERROR_MODIFIER_NAME_MISSING as _,
GenericError = PsycRenderRC::PSYC_RENDER_ERROR as _
}
impl PsycList {
/// Construct a PsycList from a list of byte lists
pub fn new(list: &[&[u8]]) -> Self {
let mut psyc_list: RawPsycList;
let elements: Vec<PsycElem>;
let mut buffer: Vec<u8>;
unsafe {
psyc_list = mem::uninitialized();
let psyc_list_ptr = &mut psyc_list as *mut RawPsycList;
elements = list.iter().map(|e| make_psyc_elem(&e)).collect();
let elements_ptr = elements.as_ptr() as *const PsycElem;
psyc_list_init(psyc_list_ptr, elements_ptr, list.len());
buffer = Vec::with_capacity(psyc_list.length);
buffer.set_len(psyc_list.length);
let buffer_ptr = buffer.as_ptr() as *mut c_char;
let _ = psyc_render_list(psyc_list_ptr, buffer_ptr, psyc_list.length);
}
PsycList {
rendered_list: buffer
}
}
/// Construct a PsycList from a list of strings (comfort function)
pub fn from_strings(list: &[&str]) -> Self {
let list_slices: Vec<&[u8]> = list.iter().map(|e| e.as_bytes()).collect();
Self::new(&list_slices)
}
}
impl PsycDict {
/// Construct a PsycDict from a list of key value pairs
pub fn new(dict: &[(&[u8], &[u8])]) -> Self {
let mut psyc_dict: RawPsycDict;
let elements: Vec<PsycDictElem>;
let mut buffer: Vec<u8>;
unsafe {
psyc_dict = mem::uninitialized();
let psyc_dict_ptr = &mut psyc_dict as *mut RawPsycDict;
elements = dict.iter().map(|e| make_psyc_dict_elem(e)).collect();
let elements_ptr = elements.as_ptr() as *const PsycDictElem;
psyc_dict_init(psyc_dict_ptr, elements_ptr, dict.len());
buffer = Vec::with_capacity(psyc_dict.length);
buffer.set_len(psyc_dict.length);
let buffer_ptr = buffer.as_ptr() as *mut c_char;
let _ = psyc_render_dict(psyc_dict_ptr, buffer_ptr, psyc_dict.length);
}
PsycDict{
rendered_dict: buffer
}
}
/// Construct a PsycDict from a list of key / value string pairs (comfort
/// function)
pub fn from_strings(dict: &[(&str, &str)]) -> Self {
let kv_list_slices: Vec<(&[u8], &[u8])> = dict.iter().map(|e| {
let &(k, v) = e;
(k.as_bytes(), v.as_bytes())
}).collect();
Self::new(&kv_list_slices)
}
}
impl<'a> PsycModifier<'a> {
/// construct a PsycModifier
pub fn new(operator: PsycOperator, name: &'a str, value: &'a [u8]) -> Self {
PsycModifier {name: name, value: value, operator: operator}
}
/// construct a PsycModifier with string value (comfort function)
pub fn with_string_value(operator: PsycOperator,
name: &'a str,
value: &'a str)
-> Self {
PsycModifier {
name: name,
value: value.as_bytes(),
operator: operator
}
}
/// construct a PsycModifier with list value
pub fn with_list_value(operator: PsycOperator,
name: &'a str,
value: &'a PsycList)
-> Self {
PsycModifier {
name: name,
value: &value.rendered_list,
operator: operator
}
}
/// construct a PsycModifier with a dictionary value (comfort function)
pub fn with_dict_value(operator: PsycOperator,
name: &'a str,
value: &'a PsycDict)
-> Self {
PsycModifier {
name: name,
value: &value.rendered_dict,
operator: operator
}
}
}
impl<'a> PsycPacket<'a> {
///
pub fn new(routing_modifiers: &'a [PsycModifier],
entity_modifiers: &'a [PsycModifier],
method: &'a str,
data: &'a [u8],
state_operator: PsycStateOp)
-> Self {
let mut packet: RawPsycPacket;
let raw_routing_modifiers: Vec<RawPsycModifier>;
let raw_entity_modifiers: Vec<RawPsycModifier>;
unsafe{
packet = mem::uninitialized();
let packet_ptr = &mut packet as *mut RawPsycPacket;
raw_routing_modifiers = routing_modifiers.iter().map(
|m| Self::make_raw_modifier(m, PsycModifierFlag::PSYC_MODIFIER_ROUTING)
).collect();
raw_entity_modifiers = entity_modifiers.iter().map(
|m| Self::make_raw_modifier(m, PsycModifierFlag::PSYC_MODIFIER_CHECK_LENGTH)
).collect();
psyc_packet_init(packet_ptr,
raw_routing_modifiers.as_ptr() as *const RawPsycModifier,
raw_routing_modifiers.len(),
raw_entity_modifiers.as_ptr() as *const RawPsycModifier,
raw_entity_modifiers.len(),
method.as_ptr() as *const c_char,
method.len(),
data.as_ptr() as *const c_char,
data.len(),
state_operator as c_char,
PsycPacketFlag::PSYC_PACKET_CHECK_LENGTH);
}
PsycPacket {
raw_packet: packet,
routing_modifiers: routing_modifiers,
entity_modifiers: entity_modifiers,
raw_routing_modifiers: raw_routing_modifiers,
raw_entity_modifiers: raw_entity_modifiers,
method: method,
body: data
}
}
pub fn new_from_raw(routing_modifiers: &'a [PsycModifier],
content: &'a [u8]) -> Self {
let mut packet: RawPsycPacket;
let raw_routing_modifiers: Vec<RawPsycModifier>;
unsafe {
packet = mem::uninitialized();
let packet_ptr = &mut packet as *mut RawPsycPacket;
raw_routing_modifiers = routing_modifiers.iter().map(
|m| Self::make_raw_modifier(m, PsycModifierFlag::PSYC_MODIFIER_ROUTING)
).collect();
psyc_packet_init_raw(packet_ptr,
raw_routing_modifiers.as_ptr() as *const RawPsycModifier,
raw_routing_modifiers.len(),
content.as_ptr() as *const c_char,
content.len(),
PsycPacketFlag::PSYC_PACKET_CHECK_LENGTH);
}
PsycPacket {
raw_packet: packet,
routing_modifiers: routing_modifiers,
entity_modifiers: &[],
raw_routing_modifiers: raw_routing_modifiers,
raw_entity_modifiers: vec![],
method: "",
body: content
}
}
/// the length of the rendered packet
pub fn length(&self) -> usize {
self.raw_packet.length
}
///
pub fn render(&self) -> Result<Vec<u8>, PsycRenderError> {
let raw_packet_ptr = & self.raw_packet as *const RawPsycPacket;
let mut buffer = Vec::with_capacity(self.length());
let buffer_ptr = buffer.as_mut_ptr() as *mut c_char;
unsafe {
buffer.set_len(self.length());
let result = psyc_render(raw_packet_ptr, buffer_ptr, buffer.capacity());
match result {
PsycRenderRC::PSYC_RENDER_SUCCESS => Ok(buffer),
_error => Err(mem::transmute(_error))
}
}
}
fn make_raw_modifier(modifier: &'a PsycModifier,
flag: PsycModifierFlag)
-> RawPsycModifier {
let mut raw_modifier: RawPsycModifier;
unsafe {
raw_modifier = mem::uninitialized();
let raw_modifier_ptr = &mut raw_modifier as *mut RawPsycModifier;
let name_ptr = modifier.name.as_ptr() as *const c_char;
let value_ptr = modifier.value.as_ptr() as *const c_char;
psyc_modifier_init(raw_modifier_ptr,
modifier.operator,
name_ptr,
modifier.name.len(),
value_ptr,
modifier.value.len(),
flag);
}
raw_modifier
}
}
unsafe fn make_psyc_elem(list_element: &[u8]) -> PsycElem {
let list_element_ptr = list_element.as_ptr() as *const c_char;
PsycElem {
elem_type: PsycString {length: 0, data: ptr::null()},
value: PsycString {length: list_element.len(), data: list_element_ptr},
length: 0,
flag: PsycElemFlag::PSYC_ELEM_CHECK_LENGTH
}
}
unsafe fn make_psyc_dict_elem(dict_element: &(&[u8], &[u8])) -> PsycDictElem {
let &(key, value) = dict_element;
let key_ptr = key.as_ptr() as *const c_char;
let psyc_dict_elem_value = make_psyc_elem(value);
let psyc_dict_elem_key = PsycDictKey {
value: PsycString {
length: key.len(),
data: key_ptr
},
length: 0,
flag: PsycElemFlag::PSYC_ELEM_CHECK_LENGTH
};
PsycDictElem {
value: psyc_dict_elem_value,
key: psyc_dict_elem_key
}
}

142
rust/src/packet_types.rs Normal file
View file

@ -0,0 +1,142 @@
use std::os::raw::c_char;
use types::*;
#[repr(C)]
pub enum PsycModifierFlag {
/// Modifier needs to be checked if it needs length.
PSYC_MODIFIER_CHECK_LENGTH = 0,
/// Modifier needs length.
PSYC_MODIFIER_NEED_LENGTH = 1,
/// Modifier doesn't need length.
PSYC_MODIFIER_NO_LENGTH = 2,
/// Routing modifier, which implies that it doesn't need length.
PSYC_MODIFIER_ROUTING = 4,
}
#[repr(C)]
pub enum PsycElemFlag {
/// Element needs to be checked if it needs length.
PSYC_ELEM_CHECK_LENGTH = 0,
/// Element needs length.
PSYC_ELEM_NEED_LENGTH = 1,
/// Element doesn't need length.
PSYC_ELEM_NO_LENGTH = 2,
}
#[repr(C)]
pub enum PsycPacketFlag {
/// Packet needs to be checked if it needs content length.
PSYC_PACKET_CHECK_LENGTH = 0,
/// Packet needs content length.
PSYC_PACKET_NEED_LENGTH = 1,
/// Packet doesn't need content length.
PSYC_PACKET_NO_LENGTH = 2,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub enum PsycOperator {
PSYC_OPERATOR_SET = ':' as _,
PSYC_OPERATOR_ASSIGN = '=' as _,
PSYC_OPERATOR_AUGMENT = '+' as _,
PSYC_OPERATOR_DIMINISH = '-' as _,
PSYC_OPERATOR_UPDATE = '@' as _,
PSYC_OPERATOR_QUERY = '?' as _,
}
#[repr(C)]
pub enum PsycStateOp {
PSYC_STATE_NOOP = 0,
PSYC_STATE_RESET = '=' as _,
PSYC_STATE_RESYNC = '?' as _,
}
#[repr(C)]
pub enum PsycPacketId {
PSYC_PACKET_ID_CONTEXT = 0,
PSYC_PACKET_ID_SOURCE = 1,
PSYC_PACKET_ID_TARGET = 2,
PSYC_PACKET_ID_COUNTER = 3,
PSYC_PACKET_ID_FRAGMENT = 4,
PSYC_PACKET_ID_ELEMS = 5,
}
#[repr(C)]
pub struct PsycElem {
pub elem_type: PsycString,
pub value: PsycString,
pub length: usize,
pub flag: PsycElemFlag
}
#[repr(C)]
pub struct PsycDictKey {
pub value: PsycString,
pub length: usize,
pub flag: PsycElemFlag
}
#[repr(C)]
pub struct PsycDictElem {
pub value: PsycElem,
pub key: PsycDictKey
}
#[repr(C)]
pub struct RawPsycDict {
dict_type: PsycString,
elems: *const PsycDictElem,
num_elems: usize,
pub length: usize
}
#[repr(C)]
pub struct RawPsycList {
list_type: PsycString,
elems: *const PsycElem,
num_elems: usize,
pub length: usize
}
#[repr(C)]
pub struct RawPsycModifier {
name: PsycString,
value: PsycString,
flag: PsycModifierFlag,
oper: c_char
}
#[repr(C)]
pub struct PsycHeader {
lines: usize,
modifiers: *mut RawPsycModifier
}
#[repr(C)]
pub struct RawPsycPacket {
routing: PsycHeader,
entity: PsycHeader,
method: PsycString,
data: PsycString,
content: PsycString,
routinglen: usize,
contentlen: usize,
pub length: usize,
stateop: PsycStateOp,
flag: PsycPacketFlag
}
#[repr(C)]
pub enum PsycRenderRC {
/// Error, method is missing, but data is present.
PSYC_RENDER_ERROR_METHOD_MISSING = -3,
/// Error, a modifier name is missing.
PSYC_RENDER_ERROR_MODIFIER_NAME_MISSING = -2,
/// Error, buffer is too small to render the packet.
PSYC_RENDER_ERROR = -1,
/// Packet is rendered successfully in the buffer.
PSYC_RENDER_SUCCESS = 0,
}

254
rust/src/parser.rs Normal file
View file

@ -0,0 +1,254 @@
use types::*;
use parser_types::*;
use std::mem;
use std::slice;
use std::os::raw::c_char;
extern "C" {
fn psyc_parse_state_init(state: *mut PsycParseState, flags: u8);
fn psyc_parse_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize);
fn psyc_parse_list_state_init(state: *mut PsycParseState);
fn psyc_parse_list_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize);
fn psyc_parse_dict_state_init(state: *mut PsycParseState);
fn psyc_parse_dict_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize);
fn psyc_parse_index_state_init(state: *mut PsycParseState);
fn psyc_parse_index_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize);
fn psyc_parse_update_state_init(state: *mut PsycParseState);
fn psyc_parse_update_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize);
fn psyc_parse_content_length(state: *mut PsycParseState) -> usize;
fn psyc_parse_content_length_found(state: *mut PsycParseState) -> bool;
fn psyc_parse_value_length(state: *mut PsycParseState) -> usize;
fn psyc_parse_value_length_found(state: *mut PsycParseState) -> bool;
fn psyc_parse_cursor(state: *mut PsycParseState) -> usize;
fn psyc_parse_buffer_length(state: *mut PsycParseState) -> usize;
fn psyc_parse_remaining_length(state: *mut PsycParseState) -> usize;
fn psyc_parse_remaining_buffer(state: *mut PsycParseState) -> *const c_char;
fn psyc_parse(state: *mut PsycParseState,
oper: *mut c_char,
name: *mut PsycString,
value: *mut PsycString)
-> PsycParseRC;
fn psyc_parse_list(state: *mut PsycParseListState,
list_type: *mut PsycString,
elem: *mut PsycString)
-> PsycParseListRC;
fn psyc_parse_dict(state: *mut PsycParseDictState,
dict_type: *mut PsycString,
elem: *mut PsycString)
-> PsycParseDictRC;
fn psyc_parse_index(state: *mut PsycParseIndexState,
idx: *mut PsycString)
-> PsycParseIndexRC;
fn psyc_parse_update(state: *mut PsycParseUpdateState,
oper: *mut c_char,
value: *mut PsycString)
-> PsycParseUpdateRC;
fn psyc_parse_uint(value: *const c_char, len: usize, n: *mut u64) -> usize;
fn psyc_parse_list_index(value: *const c_char, len: usize, n: *mut i64) -> usize;
fn psyc_is_oper(g: c_char) -> bool;
fn psyc_is_numeric(c: c_char) -> bool;
fn psyc_is_alpha(c: c_char) -> bool;
fn psyc_is_alpha_numeric(c: c_char) -> bool;
fn psyc_is_kw_char(c: c_char) -> bool;
fn psyc_is_name_char(c: c_char) -> bool;
fn psyc_is_host_char(c: c_char) -> bool;
fn psyc_parse_keyword(data: *const c_char, len: usize) -> usize;
}
pub struct PsycParser {
state: PsycParseState
}
pub struct PsycListParser {
state: PsycParseListState
}
pub struct PsycDictParser {
state: PsycParseDictState
}
pub struct PsycIndexParser {
state: PsycParseIndexState
}
pub struct PsycUpdateParser {
state: PsycParseUpdateState
}
#[derive(Debug, PartialEq)]
pub enum PsycParserResult {
StateSync,
StateReset,
ParsingComplete,
RoutingModifier {
operator: char,
name: String,
value: Vec<u8>
},
EntityModifier {
operator: char,
name: String,
value: Vec<u8>
},
IncompleteEntityModifier {
operator: char,
name: String,
value: Vec<u8>,
cursor: usize
},
Body {
name: String,
value: Vec<u8>
},
IncompleteBody {
name: String,
value: Vec<u8>,
cursor: usize
},
InsufficientData {
cursor: usize
},
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycParserError {
NoModifierLength = PsycParseRC::PSYC_PARSE_ERROR_MOD_NO_LEN as _,
NoContentLength = PsycParseRC::PSYC_PARSE_ERROR_NO_LEN as _,
NoEndDelimiter = PsycParseRC::PSYC_PARSE_ERROR_END as _,
NoNewlineAfterMethod = PsycParseRC::PSYC_PARSE_ERROR_METHOD as _,
NoNewlineAfterModifier = PsycParseRC::PSYC_PARSE_ERROR_MOD_NL as _,
InvalidModifierLength = PsycParseRC::PSYC_PARSE_ERROR_MOD_LEN as _,
NoTabBeforeModifierValue = PsycParseRC::PSYC_PARSE_ERROR_MOD_TAB as _,
NoModifierName = PsycParseRC::PSYC_PARSE_ERROR_MOD_NAME as _,
NoNewlineAfterContentLength = PsycParseRC::PSYC_PARSE_ERROR_LENGTH as _,
GenericError = PsycParseRC::PSYC_PARSE_ERROR as _,
}
impl PsycParser {
/// Create a PsycParser
pub fn new() -> Self {
let mut state: PsycParseState;
unsafe {
state = mem::uninitialized();
let state_ptr = &mut state as *mut PsycParseState;
psyc_parse_state_init(state_ptr, PsycParseFlag::PSYC_PARSE_ALL as u8)
}
PsycParser{state: state}
}
/// Set a buffer of raw bytes for parsing
pub fn set_buffer(&mut self, buffer: &[u8]) {
let state_ptr = &mut self.state as *mut PsycParseState;
let buffer_ptr = &buffer[0] as *const u8 as *const c_char;
unsafe {
psyc_parse_buffer_set(state_ptr, buffer_ptr, buffer.len())
}
}
/// Parse the buffer previously set by set_buffer. Call repeatedly until the
/// result is PsycParserResult::ParsingComplete or a PsycParserError.
pub fn parse(&mut self) -> Result<PsycParserResult, PsycParserError> {
let state_ptr = &mut self.state as *mut PsycParseState;
let mut operator: char;
let mut name: PsycString;
let mut value: PsycString;
unsafe {
operator = mem::uninitialized();
name = mem::uninitialized();
value = mem::uninitialized();
let operator_ptr = &mut operator as *mut char as *mut c_char;
let name_ptr = &mut name as *mut PsycString;
let value_ptr = &mut value as *mut PsycString;
let parse_result = psyc_parse(state_ptr, operator_ptr, name_ptr, value_ptr);
match parse_result {
PsycParseRC::PSYC_PARSE_INSUFFICIENT => {
let result =
PsycParserResult::InsufficientData {
cursor: psyc_parse_cursor(state_ptr)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_ROUTING => {
let result =
PsycParserResult::RoutingModifier {
operator: operator,
name: Self::cstring_to_string(name.data, name.length),
value: Self::cstring_to_bytes(value.data, value.length)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_ENTITY |
PsycParseRC::PSYC_PARSE_ENTITY_END => {
let result =
PsycParserResult::EntityModifier {
operator: operator,
name: Self::cstring_to_string(name.data, name.length),
value: Self::cstring_to_bytes(value.data, value.length)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_ENTITY_START |
PsycParseRC::PSYC_PARSE_ENTITY_CONT => {
let result =
PsycParserResult::IncompleteEntityModifier {
operator: operator,
name: Self::cstring_to_string(name.data, name.length),
value: Self::cstring_to_bytes(value.data, value.length),
cursor: psyc_parse_cursor(state_ptr)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_BODY |
PsycParseRC::PSYC_PARSE_BODY_END => {
let result =
PsycParserResult::Body {
name: Self::cstring_to_string(name.data, name.length),
value: Self::cstring_to_bytes(value.data, value.length)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_BODY_START |
PsycParseRC::PSYC_PARSE_BODY_CONT => {
let result =
PsycParserResult::IncompleteBody {
name: Self::cstring_to_string(name.data, name.length),
value: Self::cstring_to_bytes(value.data, value.length),
cursor: psyc_parse_cursor(state_ptr)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_STATE_RESYNC =>
Ok(PsycParserResult::StateSync),
PsycParseRC::PSYC_PARSE_STATE_RESET =>
Ok(PsycParserResult::StateReset),
PsycParseRC::PSYC_PARSE_COMPLETE =>
Ok(PsycParserResult::ParsingComplete),
_error => Err(mem::transmute(_error)),
}
}
}
unsafe fn cstring_to_string(cstring: *const c_char, length: usize) -> String {
let vec = Self::cstring_to_bytes(cstring, length);
String::from_utf8(vec).unwrap()
}
unsafe fn cstring_to_bytes(cstring: *const c_char, length: usize) -> Vec<u8> {
slice::from_raw_parts(cstring as *const u8, length).to_vec()
}
}

300
rust/src/parser_types.rs Normal file
View file

@ -0,0 +1,300 @@
#![allow(non_camel_case_types)]
use types::*;
enum PsycPart { }
enum PsycListPart { }
enum PsycDictPart { }
enum PsycIndexPart { }
enum PsycUpdatePart { }
#[repr(C)]
pub struct PsycParseState {
buffer: PsycString,
cursor: usize,
startc: usize,
routinglen: usize,
contentlen: usize,
content_parsed: usize,
valuelen: usize,
value_parsed: usize,
part: PsycPart,
flags: u8,
contentlen_found: u8,
valuelen_found: u8
}
#[repr(C)]
pub struct PsycParseListState {
buffer: PsycString,
cursor: usize,
startc: usize,
list_type: PsycString,
elemlen: usize,
elem_parsed: usize,
part: PsycListPart,
elemlen_found: u8
}
#[repr(C)]
pub struct PsycParseDictState {
buffer: PsycString,
cursor: usize,
startc: usize,
elemlen: usize,
elem_parsed: usize,
part: PsycDictPart,
elemlen_found: u8
}
#[repr(C)]
pub struct PsycParseIndexState {
buffer: PsycString,
cursor: usize,
startc: usize,
elemlen: usize,
elem_parsed: usize,
part: PsycIndexPart,
elemlen_found: u8
}
#[repr(C)]
pub struct PsycParseUpdateState {
buffer: PsycString,
cursor: usize,
startc: usize,
elemlen: usize,
elem_parsed: usize,
part: PsycUpdatePart,
elemlen_found: u8
}
#[repr(C)]
pub enum PsycParseFlag {
/// Default Flag. Parse everything.
PSYC_PARSE_ALL = 0,
/// Parse only the header
PSYC_PARSE_ROUTING_ONLY = 1,
/// Parse only the content.
/// Parsing starts at the content and the content must be complete.
PSYC_PARSE_START_AT_CONTENT = 2,
}
#[repr(C)]
pub enum PsycParseRC {
/// Error, no length is set for a modifier which is longer than PSYC_MODIFIER_SIZE_THRESHOLD.
PSYC_PARSE_ERROR_MOD_NO_LEN = -10,
/// Error, no length is set for the content but it is longer than PSYC_CONTENT_SIZE_THRESHOLD.
PSYC_PARSE_ERROR_NO_LEN = -9,
/// Error, packet is not ending with a valid delimiter.
PSYC_PARSE_ERROR_END = -8,
/// Error, expected NL after the method.
PSYC_PARSE_ERROR_METHOD = -7,
/// Error, expected NL after a modifier.
PSYC_PARSE_ERROR_MOD_NL = -6,
/// Error, modifier length is not numeric.
PSYC_PARSE_ERROR_MOD_LEN = -5,
/// Error, expected TAB before modifier value.
PSYC_PARSE_ERROR_MOD_TAB = -4,
/// Error, modifier name is missing.
PSYC_PARSE_ERROR_MOD_NAME = -3,
/// Error, expected NL after the content length.
PSYC_PARSE_ERROR_LENGTH = -2,
/// Error in packet.
PSYC_PARSE_ERROR = -1,
/// Buffer contains insufficient amount of data.
/// Fill another buffer and concatenate it with the end of the current buffer,
/// from the cursor position to the end.
PSYC_PARSE_INSUFFICIENT = 1,
/// Routing modifier parsing done.
/// Operator, name & value contains the respective parts.
PSYC_PARSE_ROUTING = 2,
/// State sync operation.
PSYC_PARSE_STATE_RESYNC = 3,
/// State reset operation.
PSYC_PARSE_STATE_RESET = 4,
/// Start of an incomplete entity modifier.
/// Operator & name are complete, value is incomplete.
PSYC_PARSE_ENTITY_START = 5,
/// Continuation of an incomplete entity modifier.
PSYC_PARSE_ENTITY_CONT = 6,
/// End of an incomplete entity modifier.
PSYC_PARSE_ENTITY_END = 7,
/// Entity modifier parsing done in one go.
/// Operator, name & value contains the respective parts.
PSYC_PARSE_ENTITY = 8,
/// Start of an incomplete body.
/// Name contains method, value contains part of the body.
/// Used when packet length is given
PSYC_PARSE_BODY_START = 9,
/// Continuation of an incomplete body.
/// Used when packet length is given
PSYC_PARSE_BODY_CONT = 10,
/// End of an incomplete body.
/// Used when packet length is given
PSYC_PARSE_BODY_END = 11,
/// Body parsing done in one go, name contains method, value contains body.
PSYC_PARSE_BODY = 12,
///// Start of an incomplete content, value contains part of content.
///// Used when PSYC_PARSE_ROUTING_ONLY is set.
//PSYC_PARSE_CONTENT_START = 9,
///// Continuation of an incomplete content.
///// Used when PSYC_PARSE_ROUTING_ONLY is set.
//PSYC_PARSE_CONTENT_CONT = 10,
///// End of an incomplete content.
///// Used when PSYC_PARSE_ROUTING_ONLY is set.
//PSYC_PARSE_CONTENT_END = 11,
///// Content parsing done in one go, value contains the whole content.
///// Used when PSYC_PARSE_ROUTING_ONLY is set.
//PSYC_PARSE_CONTENT = 12,
/// Finished parsing packet.
PSYC_PARSE_COMPLETE = 13,
}
#[repr(C)]
pub enum PsycParseListRC {
/// Error, no length is set for an element which is longer than PSYC_ELEM_SIZE_THRESHOLD.
PSYC_PARSE_LIST_ERROR_ELEM_NO_LEN = -6,
PSYC_PARSE_LIST_ERROR_ELEM_LENGTH = -5,
PSYC_PARSE_LIST_ERROR_ELEM_TYPE = -4,
PSYC_PARSE_LIST_ERROR_ELEM_START = -3,
PSYC_PARSE_LIST_ERROR_TYPE = -2,
PSYC_PARSE_LIST_ERROR = -1,
/// Reached end of buffer.
/// Buffer contains insufficient amount of data.
/// Fill another buffer and concatenate it with the end of the current buffer,
/// from the cursor position to the end.
PSYC_PARSE_LIST_INSUFFICIENT = 1,
/// Completed parsing the default type of the list.
PSYC_PARSE_LIST_TYPE = 2,
/// Start of an element is parsed but still not complete.
PSYC_PARSE_LIST_ELEM_START = 3,
/// Continuation of an incomplete element.
PSYC_PARSE_LIST_ELEM_CONT = 4,
/// Element parsing completed.
PSYC_PARSE_LIST_ELEM_END = 5,
/// Completed parsing a list element.
PSYC_PARSE_LIST_ELEM = 6,
/// Completed parsing the last element in the list.
PSYC_PARSE_LIST_ELEM_LAST = 7,
/// Reached end of buffer.
PSYC_PARSE_LIST_END = 8,
}
#[repr(C)]
pub enum PsycParseDictRC {
PSYC_PARSE_DICT_ERROR_VALUE = -9,
PSYC_PARSE_DICT_ERROR_VALUE_LENGTH = -8,
PSYC_PARSE_DICT_ERROR_VALUE_TYPE = -7,
PSYC_PARSE_DICT_ERROR_VALUE_START = -6,
PSYC_PARSE_DICT_ERROR_KEY = -5,
PSYC_PARSE_DICT_ERROR_KEY_LENGTH = -4,
PSYC_PARSE_DICT_ERROR_KEY_START = -3,
PSYC_PARSE_DICT_ERROR_TYPE = -2,
PSYC_PARSE_DICT_ERROR = -1,
/// Reached end of buffer.
/// Buffer contains insufficient amount of data.
/// Fill another buffer and concatenate it with the end of the current buffer,
/// from the cursor position to the end.
PSYC_PARSE_DICT_INSUFFICIENT = 1,
/// Completed parsing the default type of the dict.
PSYC_PARSE_DICT_TYPE = 2,
/// Start of a key is parsed but still not complete.
PSYC_PARSE_DICT_KEY_START = 3,
/// Continuation of an incomplete key.
PSYC_PARSE_DICT_KEY_CONT = 4,
/// Last continuation of a key.
PSYC_PARSE_DICT_KEY_END = 5,
/// Completed parsing a key in one go.
PSYC_PARSE_DICT_KEY = 6,
/// Start of a value is parsed but still not complete.
PSYC_PARSE_DICT_VALUE_START = 7,
/// Continuation of an incomplete value.
PSYC_PARSE_DICT_VALUE_CONT = 8,
/// Last continuation of a value.
PSYC_PARSE_DICT_VALUE_END = 9,
/// Completed parsing a value.
PSYC_PARSE_DICT_VALUE = 10,
/// Completed parsing the last value.
PSYC_PARSE_DICT_VALUE_LAST = 11,
/// Reached end of buffer.
PSYC_PARSE_DICT_END = 12,
}
#[repr(C)]
pub enum PsycParseIndexRC {
PSYC_PARSE_INDEX_ERROR_DICT = -6,
PSYC_PARSE_INDEX_ERROR_DICT_LENGTH = -5,
PSYC_PARSE_INDEX_ERROR_STRUCT = -4,
PSYC_PARSE_INDEX_ERROR_LIST = -3,
PSYC_PARSE_INDEX_ERROR_TYPE = -2,
PSYC_PARSE_INDEX_ERROR = -1,
/// Reached end of buffer.
/// Buffer contains insufficient amount of data.
/// Fill another buffer and concatenate it with the end of the current buffer,
/// from the cursor position to the end.
PSYC_PARSE_INDEX_INSUFFICIENT = 1,
// Completed parsing a list index.
PSYC_PARSE_INDEX_LIST = 3,
// Completed parsing a list index at the end of the buffer.
PSYC_PARSE_INDEX_LIST_LAST = 4,
// Completed parsing a struct element name.
PSYC_PARSE_INDEX_STRUCT = 5,
// Completed parsing a struct element name at the end of the buffer.
PSYC_PARSE_INDEX_STRUCT_LAST = 6,
/// Start of a dict key is parsed but still not complete.
PSYC_PARSE_INDEX_DICT_START = 7,
/// Continuation of an incomplete dict key.
PSYC_PARSE_INDEX_DICT_CONT = 8,
/// Last continuation of a dict key.
PSYC_PARSE_INDEX_DICT_END = 9,
/// Completed parsing a dict key in one go.
PSYC_PARSE_INDEX_DICT = 10,
/// Reached end of buffer.
PSYC_PARSE_INDEX_END = 11,
}
#[repr(C)]
pub enum PsycParseUpdateRC {
PSYC_PARSE_UPDATE_ERROR_VALUE = -24,
PSYC_PARSE_UPDATE_ERROR_LENGTH = -23,
PSYC_PARSE_UPDATE_ERROR_TYPE = -22,
PSYC_PARSE_UPDATE_ERROR_OPER = -21,
PSYC_PARSE_UPDATE_ERROR = -1,
/// Reached end of buffer.
/// Buffer contains insufficient amount of data.
/// Fill another buffer and concatenate it with the end of the current buffer,
/// from the cursor position to the end.
PSYC_PARSE_UPDATE_INSUFFICIENT = 1,
// Completed parsing a list index.
PSYC_PARSE_UPDATE_INDEX_LIST = 3,
// Completed parsing a struct element name.
PSYC_PARSE_UPDATE_INDEX_STRUCT = 5,
/// Start of a dict key is parsed but still not complete.
PSYC_PARSE_UPDATE_INDEX_DICT_START = 7,
/// Continuation of an incomplete dict key.
PSYC_PARSE_UPDATE_INDEX_DICT_CONT = 8,
/// Last continuation of a dict key.
PSYC_PARSE_UPDATE_INDEX_DICT_END = 9,
/// Completed parsing a dict key in one go.
PSYC_PARSE_UPDATE_INDEX_DICT = 10,
/// Completed parsing the type.
PSYC_PARSE_UPDATE_TYPE = 21,
/// Completed parsing the type and reached end of buffer.
PSYC_PARSE_UPDATE_TYPE_END = 22,
/// Start of the value is parsed but still not complete.
PSYC_PARSE_UPDATE_VALUE_START = 23,
/// Continuation of incomplete value.
PSYC_PARSE_UPDATE_VALUE_CONT = 24,
/// Last continuation of the value.
PSYC_PARSE_UPDATE_VALUE_END = 25,
/// Completed parsing the value in one go.
PSYC_PARSE_UPDATE_VALUE = 26,
/// Reached end of buffer.
PSYC_PARSE_UPDATE_END = 27,
}

9
rust/src/types.rs Normal file
View file

@ -0,0 +1,9 @@
use std::os::raw::c_char;
#[repr(C)]
pub struct PsycString {
pub length: usize,
pub data: *const c_char
}

97
rust/tests/test_packet.rs Normal file
View file

@ -0,0 +1,97 @@
extern crate psyc;
use psyc::*;
use psyc::packet_types::*;
#[test]
fn test_create_render() {
let r1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET,
"_target",
"psyc://ve.symlynx.com/@blog".as_bytes());
let r2 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET,
"_tag",
"666666".as_bytes());
let e1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET,
"_nick",
"lurchi".as_bytes());
let routing_modifiers = vec![r1, r2];
let entity_modifiers = vec![e1];
let data = vec![];
let packet = PsycPacket::new(&routing_modifiers,
&entity_modifiers,
"_request_context_enter",
&data,
PsycStateOp::PSYC_STATE_NOOP);
let expected = ":_target\tpsyc://ve.symlynx.com/@blog\n:_tag\t666666\n\n:_nick\tlurchi\n_request_context_enter\n|\n".as_bytes().to_vec();
let rendered_packet = packet.render();
assert_eq!(rendered_packet, Ok(expected))
}
#[test]
fn test_list() {
let r1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET,
"_target",
"psyc://ve.symlynx.com/@blog".as_bytes());
let list = PsycList::from_strings(&["str1", "str2", "str3"]);
let e1 = PsycModifier::with_list_value(PsycOperator::PSYC_OPERATOR_SET,
"_list_test",
&list);
let routing_modifiers = vec![r1];
let entity_modifiers = vec![e1];
let data = vec![];
let packet = PsycPacket::new(&routing_modifiers,
&entity_modifiers,
"",
&data,
PsycStateOp::PSYC_STATE_NOOP);
let expected = ":_target\tpsyc://ve.symlynx.com/@blog\n34\n:_list_test 18\t| str1| str2| str3\n|\n".as_bytes().to_vec();
let rendered_packet = packet.render();
assert_eq!(rendered_packet, Ok(expected));
}
#[test]
fn test_dict() {
let r1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET,
"_target",
"psyc://ve.symlynx.com/@blog".as_bytes());
let dict = PsycDict::from_strings(&[("key1", "value1"),
("key2", "value2"),
("key3", "value3")]);
let e1 = PsycModifier::with_dict_value(PsycOperator::PSYC_OPERATOR_SET,
"_dict_test",
&dict);
let routing_modifiers = vec![r1];
let entity_modifiers = vec![e1];
let data = vec![];
let packet = PsycPacket::new(&routing_modifiers,
&entity_modifiers,
"",
&data,
PsycStateOp::PSYC_STATE_NOOP);
let expected = ":_target\tpsyc://ve.symlynx.com/@blog\n58\n:_dict_test 42\t{ key1} value1{ key2} value2{ key3} value3\n|\n".as_bytes().to_vec();
let rendered_packet = packet.render();
//println!("rendered: {}", String::from_utf8(rendered_packet.unwrap()).unwrap());
assert_eq!(rendered_packet, Ok(expected));
}

26
rust/tests/test_parser.rs Normal file
View file

@ -0,0 +1,26 @@
extern crate psyc;
use psyc::parser::*;
#[test]
fn test_parse() {
let test_data = ":_target\tpsyc://ve.symlynx.com/@blog\n\n?\n|\n".to_string().into_bytes();
let expected1 =
PsycParserResult::RoutingModifier{
operator: ':',
name: "_target".to_string(),
value: "psyc://ve.symlynx.com/@blog".to_string().into_bytes()
};
let expected2 = PsycParserResult::StateSync;
let mut parser = PsycParser::new();
parser.set_buffer(&test_data);
let result1 = parser.parse();
let result2 = parser.parse();
assert_eq!(result1, Ok(expected1));
assert_eq!(result2, Ok(expected2));
}