Merge remote-tracking branch 'lurchi/master'

This commit is contained in:
psyc://loupsycedyglgamf.onion/~lynX 1984-04-04 00:44:04 +00:00
commit 2e82f31f9d
24 changed files with 864 additions and 166 deletions

View File

@ -4,3 +4,6 @@ version = "1.0.0"
links = "psyc"
build = "build.rs"
description = "rust bindings for libpsyc"
[dependencies]
bitflags = "0.7.0"

36
rust/src/keyword.rs Normal file
View File

@ -0,0 +1,36 @@
use std::os::raw::{c_char, c_int};
extern "C" {
fn psyc_inherits(sho: *const c_char,
slen: usize,
lon: *const c_char,
llen: usize)
-> c_int;
fn psyc_matches(sho: *const c_char,
slen: usize,
lon: *const c_char,
llen: usize)
-> c_int;
}
pub trait Keyword {
fn keyword(&self) -> &[u8];
fn inherits(&self, other: &[u8]) -> bool {
let keyword_ptr = self.keyword().as_ptr() as *const c_char;
let other_ptr = other.as_ptr() as *const c_char;
let result = unsafe {
psyc_inherits(other_ptr, other.len(), keyword_ptr, self.keyword().len())
};
result == 0
}
fn matches(&self, other: &[u8]) -> bool {
let keyword_ptr = self.keyword().as_ptr() as *const c_char;
let other_ptr = other.as_ptr() as *const c_char;
let result = unsafe {
psyc_matches(other_ptr, other.len(), keyword_ptr, self.keyword().len())
};
result == 0
}
}

View File

@ -1,12 +1,33 @@
#![allow(dead_code)]
#[macro_use]
extern crate bitflags;
mod keyword;
mod method_types;
mod packet_types;
mod parser_types;
mod types;
mod uniform_types;
mod util;
pub mod parser_types;
pub mod packet_types;
mod variable_types;
pub mod method;
pub mod parser;
pub mod packet;
pub mod packet_id;
pub mod uniform;
pub mod variable;
pub use parser::*;
pub use packet::*;
pub use packet_id::*;
pub use keyword::Keyword;
pub use method::Method;
pub use method_types::{PsycMethod, MethodInfo, PsycMethodFlags};
pub use method_types::{PSYC_METHOD_TEMPLATE, PSYC_METHOD_REPLY, PSYC_METHOD_VISIBLE, PSYC_METHOD_LOGGABLE, PSYC_METHOD_MANUAL};
pub use parser::{PsycParser, PsycListParser, Parser};
pub use parser_types::{PsycParserResult, PsycListParserResult, PsycParserError, PsycListParserError};
pub use packet::{PsycList, PsycModifier, PsycPacket};
pub use packet_id::PacketId;
pub use packet_types::PacketRenderError;
pub use uniform::Uniform;
pub use uniform_types::{UniformParseError, PsycScheme, PsycEntity};
pub use packet_types::{PsycOperator, PsycStateOp};
pub use variable::{RoutingVariable, EntityVariable, Variable};
pub use variable_types::{PsycRoutingVar, PsycType};

45
rust/src/method.rs Normal file
View File

@ -0,0 +1,45 @@
use method_types::{PsycMethod, PsycMethodFlags, MethodInfo};
use keyword::Keyword;
use std::mem;
use std::os::raw::{c_char, c_uint};
extern "C" {
fn psyc_method(method: *const c_char,
methodlen: usize,
family: *mut PsycMethod,
flag: *mut c_uint)
-> PsycMethod;
}
pub struct Method<'a> {
pub method: &'a [u8]
}
impl<'a> Keyword for Method<'a> {
fn keyword(&self) -> &[u8] {
self.method
}
}
impl<'a> Method<'a> {
pub fn lookup(&self) -> MethodInfo {
let method_ptr = self.method.as_ptr() as *const c_char;
let lookup_result: PsycMethod;
let mut family: PsycMethod;
let mut flags: PsycMethodFlags;
unsafe {
family = mem::uninitialized();
flags = mem::uninitialized();
let flags_ptr = &mut flags as *mut PsycMethodFlags as *mut c_uint;
lookup_result = psyc_method(method_ptr,
self.method.len(),
&mut family as *mut PsycMethod,
flags_ptr)
}
MethodInfo {
lookup_result: lookup_result,
family: family,
flags: flags
}
}
}

60
rust/src/method_types.rs Normal file
View File

@ -0,0 +1,60 @@
use std::os::raw::c_int;
bitflags! {
pub flags PsycMethodFlags: c_int {
const PSYC_METHOD_TEMPLATE = 1 << 0,
const PSYC_METHOD_REPLY = 1 << 1,
const PSYC_METHOD_VISIBLE = 1 << 2,
const PSYC_METHOD_LOGGABLE = 1 << 3,
const PSYC_METHOD_MANUAL = 1 << 4
}
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycMethod {
PSYC_MC_UNKNOWN,
PSYC_MC_CONVERSE,
PSYC_MC_DATA,
PSYC_MC_ECHO,
PSYC_MC_ECHO_CONTEXT_ENTER,
PSYC_MC_ECHO_CONTEXT_LEAVE,
PSYC_MC_ECHO_HELLO,
PSYC_MC_ERROR,
PSYC_MC_FAILURE,
PSYC_MC_FAILURE_ALIAS_NONEXISTANT,
PSYC_MC_FAILURE_ALIAS_UNAVAILABLE,
PSYC_MC_INFO,
PSYC_MC_MESSAGE, // deprecated, use _converse
PSYC_MC_MESSAGE_ACTION, // deprecated, use _converse
PSYC_MC_NOTICE,
PSYC_MC_NOTICE_ALIAS_ADD,
PSYC_MC_NOTICE_ALIAS_CHANGE,
PSYC_MC_NOTICE_ALIAS_REMOVE,
PSYC_MC_NOTICE_CONTEXT_ENTER,
PSYC_MC_NOTICE_CONTEXT_LEAVE,
PSYC_MC_NOTICE_FRIENDSHIP,
PSYC_MC_NOTICE_LINK,
PSYC_MC_NOTICE_PEER_CONNECT,
PSYC_MC_NOTICE_PEER_DISCONNECT,
PSYC_MC_NOTICE_SET,
PSYC_MC_NOTICE_UNLINK,
PSYC_MC_REQUEST,
PSYC_MC_REQUEST_CONTEXT_ENTER,
PSYC_MC_REQUEST_CONTEXT_LEAVE,
PSYC_MC_REQUEST_FRIENDSHIP,
PSYC_MC_STATUS,
PSYC_MC_STATUS_CONTEXTS_ENTERED,
PSYC_MC_STATUS_HELLO,
PSYC_MC_WARNING,
PSYC_METHODS_NUM,
}
#[derive(Debug, PartialEq)]
pub struct MethodInfo {
pub lookup_result: PsycMethod,
pub family: PsycMethod,
pub flags: PsycMethodFlags
}

View File

@ -1,6 +1,6 @@
use types::*;
use types::PsycString;
use packet_types::*;
use packet_id::*;
use packet_id::PacketId;
use util;
use std::mem;
use std::ptr;
@ -70,14 +70,6 @@ pub struct PsycPacket<'a> {
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 {
@ -212,7 +204,7 @@ impl<'a> PsycPacket<'a> {
}
///
pub fn render(&self) -> Result<Vec<u8>, PsycRenderError> {
pub fn render(&self) -> Result<Vec<u8>, PacketRenderError> {
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;

View File

@ -1,5 +1,6 @@
use packet_types::{RawPsycList, PsycElem};
use parser::{PsycListParser, PsycListParserResult};
use parser::PsycListParser;
use parser_types::PsycListParserResult;
use util;
use std::mem;
use std::ptr;

View File

@ -33,6 +33,16 @@ pub enum PsycPacketFlag {
PSYC_PACKET_NO_LENGTH = 2,
}
#[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,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub enum PsycOperator {
@ -51,16 +61,6 @@ pub enum PsycStateOp {
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,
@ -116,3 +116,11 @@ pub enum PsycRenderRC {
/// Packet is rendered successfully in the buffer.
PSYC_RENDER_SUCCESS = 0,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PacketRenderError {
MethodMissing = PsycRenderRC::PSYC_RENDER_ERROR_METHOD_MISSING as _,
ModifierNameMissing = PsycRenderRC::PSYC_RENDER_ERROR_MODIFIER_NAME_MISSING as _,
GenericError = PsycRenderRC::PSYC_RENDER_ERROR as _
}

View File

@ -1,4 +1,4 @@
use types::*;
use types::PsycString;
use parser_types::*;
use util;
use std::mem;
@ -31,93 +31,6 @@ pub struct PsycListParser {
state: PsycParseListState
}
#[derive(Debug, PartialEq)]
pub enum PsycParserResult<'a> {
StateSync,
StateReset,
Complete,
InsufficientData,
RoutingModifier {
operator: char,
name: &'a [u8],
value: &'a [u8]
},
EntityModifier {
operator: char,
name: &'a [u8],
value: &'a [u8]
},
EntityModifierStart {
operator: char,
name: &'a [u8],
value_part: &'a [u8]
},
EntityModifierCont {
value_part: &'a [u8]
},
EntityModifierEnd {
value_part: &'a [u8]
},
Body {
name: &'a [u8],
value: &'a [u8]
},
BodyStart {
name: &'a [u8],
value_part: &'a [u8]
},
BodyCont {
value_part: &'a [u8]
},
BodyEnd {
value_part: &'a [u8]
}
}
#[derive(Debug, PartialEq)]
pub enum PsycListParserResult<'a> {
Complete,
InsufficientData,
ListElement {
value: &'a [u8]
},
ListElementStart {
value_part: &'a [u8]
},
ListElementCont {
value_part: &'a [u8]
},
ListElementEnd {
value_part: &'a [u8]
}
}
#[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 _,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycListParserError {
NoElementLength = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_NO_LEN as _,
InvalidElementLength = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_LENGTH as _,
InvalidElementType = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_TYPE as _,
InvalidElementStart = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_START as _,
InvalidType = PsycParseListRC::PSYC_PARSE_LIST_ERROR_TYPE as _,
GenericError = PsycParseListRC::PSYC_PARSE_LIST_ERROR as _,
}
impl PsycParser {
/// Create a PsycParser
pub fn new() -> Self {
@ -207,29 +120,29 @@ impl PsycParser {
PsycParseRC::PSYC_PARSE_BODY => {
let result = PsycParserResult::Body {
name: util::cstring_to_slice(name.data, name.length),
value: util::cstring_to_slice(value.data, value.length)
method: util::cstring_to_slice(name.data, name.length),
data: util::cstring_to_slice(value.data, value.length)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_BODY_START => {
let result = PsycParserResult::BodyStart {
name: util::cstring_to_slice(name.data, name.length),
value_part: util::cstring_to_slice(value.data, value.length)
method: util::cstring_to_slice(name.data, name.length),
data_part: util::cstring_to_slice(value.data, value.length)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_BODY_CONT => {
let result = PsycParserResult::BodyCont {
value_part: util::cstring_to_slice(value.data, value.length)
data_part: util::cstring_to_slice(value.data, value.length)
};
Ok(result)
},
PsycParseRC::PSYC_PARSE_BODY_END => {
let result = PsycParserResult::BodyEnd {
value_part: util::cstring_to_slice(value.data, value.length)
data_part: util::cstring_to_slice(value.data, value.length)
};
Ok(result)
}
@ -300,21 +213,24 @@ impl PsycListParser {
PsycParseListRC::PSYC_PARSE_LIST_ELEM_START => {
let result = PsycListParserResult::ListElementStart {
value_part: util::cstring_to_slice(element.data, element.length)
value_part: util::cstring_to_slice(element.data,
element.length)
};
return Ok(result)
},
PsycParseListRC::PSYC_PARSE_LIST_ELEM_CONT => {
let result = PsycListParserResult::ListElementCont {
value_part: util::cstring_to_slice(element.data, element.length)
value_part: util::cstring_to_slice(element.data,
element.length)
};
return Ok(result)
},
PsycParseListRC::PSYC_PARSE_LIST_ELEM_END => {
let result = PsycListParserResult::ListElementEnd {
value_part: util::cstring_to_slice(element.data, element.length)
value_part: util::cstring_to_slice(element.data,
element.length)
};
return Ok(result)
},

View File

@ -1,5 +1,5 @@
#![allow(non_camel_case_types)]
use types::*;
use types::PsycString;
enum PsycPart { }
enum PsycListPart { }
@ -35,39 +35,6 @@ pub struct PsycParseListState {
elemlen_found: u8
}
#[repr(C)]
pub struct PsycParseDictState {
pub buffer: PsycString,
pub cursor: usize,
startc: usize,
elemlen: usize,
elem_parsed: usize,
part: PsycDictPart,
elemlen_found: u8
}
#[repr(C)]
pub struct PsycParseIndexState {
buffer: PsycString,
pub cursor: usize,
startc: usize,
elemlen: usize,
elem_parsed: usize,
part: PsycIndexPart,
elemlen_found: u8
}
#[repr(C)]
pub struct PsycParseUpdateState {
buffer: PsycString,
pub cursor: usize,
startc: usize,
elemlen: usize,
elem_parsed: usize,
part: PsycUpdatePart,
elemlen_found: u8
}
#[repr(C)]
pub enum PsycParseFlag {
/// Default Flag. Parse everything.
@ -298,6 +265,89 @@ pub enum PsycParseUpdateRC {
PSYC_PARSE_UPDATE_END = 27,
}
#[derive(Debug, PartialEq)]
pub enum PsycParserResult<'a> {
StateSync,
StateReset,
Complete,
InsufficientData,
RoutingModifier {
operator: char,
name: &'a [u8],
value: &'a [u8]
},
EntityModifier {
operator: char,
name: &'a [u8],
value: &'a [u8]
},
EntityModifierStart {
operator: char,
name: &'a [u8],
value_part: &'a [u8]
},
EntityModifierCont {
value_part: &'a [u8]
},
EntityModifierEnd {
value_part: &'a [u8]
},
Body {
method: &'a [u8],
data: &'a [u8]
},
BodyStart {
method: &'a [u8],
data_part: &'a [u8]
},
BodyCont {
data_part: &'a [u8]
},
BodyEnd {
data_part: &'a [u8]
}
}
#[derive(Debug, PartialEq)]
pub enum PsycListParserResult<'a> {
Complete,
InsufficientData,
ListElement {
value: &'a [u8]
},
ListElementStart {
value_part: &'a [u8]
},
ListElementCont {
value_part: &'a [u8]
},
ListElementEnd {
value_part: &'a [u8]
}
}
#[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 _,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycListParserError {
NoElementLength = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_NO_LEN as _,
InvalidElementLength = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_LENGTH as _,
InvalidElementType = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_TYPE as _,
InvalidElementStart = PsycParseListRC::PSYC_PARSE_LIST_ERROR_ELEM_START as _,
InvalidType = PsycParseListRC::PSYC_PARSE_LIST_ERROR_TYPE as _,
GenericError = PsycParseListRC::PSYC_PARSE_LIST_ERROR as _,
}

View File

@ -1,9 +1,30 @@
use std::os::raw::c_char;
/// Return code: OK/error.
#[repr(C)]
pub enum PsycRC {
PSYC_OK = 1,
PSYC_ERROR = -1,
}
#[repr(C)]
pub enum PsycBool {
PSYC_FALSE = 0,
PSYC_TRUE = 1,
}
#[derive(Debug)]
#[repr(C)]
pub struct PsycString {
pub length: usize,
pub data: *const c_char
}
impl PsycBool {
pub fn to_bool(self) -> bool {
match self {
PsycBool::PSYC_FALSE => false,
PsycBool::PSYC_TRUE => true
}
}
}

188
rust/src/uniform.rs Normal file
View File

@ -0,0 +1,188 @@
use types::PsycRC;
use uniform_types::*;
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::marker::PhantomData;
use std::mem;
use util;
extern "C" {
fn psyc_uniform_parse(uniform: *mut PsycUniform,
buffer: *const c_char,
length: usize)
-> c_int;
fn psyc_entity_type(entity: c_char) -> c_int;
}
#[derive(Debug)]
pub struct Uniform<'a> {
uniform: PsycUniform,
phantom: PhantomData<&'a Vec<u8>>
}
impl<'a> Uniform<'a> {
pub fn from_bytes(buffer: &'a [u8]) -> Result<Self, UniformParseError> {
let buffer_ptr = buffer.as_ptr() as *const c_char;
let mut uniform: PsycUniform;
unsafe {
uniform = mem::zeroed();
let uniform_ptr = &mut uniform as *mut PsycUniform;
let parse_result = psyc_uniform_parse(uniform_ptr, buffer_ptr, buffer.len());
if parse_result < PsycScheme::PSYC_SCHEME_PSYC as _ {
return Err(mem::transmute(parse_result))
}
}
let result = Uniform {
uniform: uniform,
phantom: PhantomData
};
Ok(result)
}
//pub fn from_raw_uniform(uniform: PsycUniform) -> Self {
// UniformRef {
// uniform: uniform,
// phantom: PhantomData
// }
//}
pub fn is_valid(&self) -> bool {
self.uniform.valid
}
pub fn entity(&self) -> PsycEntity<'a> {
match self.resource() {
"" => PsycEntity::Root,
_resource => unsafe {
let type_specifier = *self.uniform.resource.data;
let entity_type_int = psyc_entity_type(type_specifier);
if entity_type_int == PsycRC::PSYC_ERROR as c_int {
return PsycEntity::Unknown {
object: self.resource(),
channel: self.channel()
}
}
let entity_type: PsycEntityType = mem::transmute(entity_type_int);
match entity_type {
PsycEntityType::PSYC_ENTITY_PERSON => PsycEntity::Person {
name: self.nick(),
channel: self.channel()
},
PsycEntityType::PSYC_ENTITY_PLACE => PsycEntity::Place {
name: self.nick(),
channel: self.channel()
},
PsycEntityType::PSYC_ENTITY_SERVICE => PsycEntity::Service {
name: self.nick(),
channel: self.channel()
},
PsycEntityType::PSYC_ENTITY_ROOT => PsycEntity::Root
}
}
}
}
pub fn scheme(&self) -> PsycScheme {
self.uniform.uniform_type
}
pub fn password(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.pass.data, self.uniform.pass.length)
}
}
pub fn host(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.host.data, self.uniform.host.length)
}
}
pub fn port(&self) -> Option<i32> {
let slice: &'a str;
unsafe {
slice = util::cstring_to_str(self.uniform.port.data,
self.uniform.port.length)
}
match slice {
"" => None,
s => Some(s.parse::<i32>().unwrap())
}
}
pub fn transport(&self) -> Option<PsycTransport> {
let slice: &'a str;
unsafe {
slice = util::cstring_to_str(self.uniform.transport.data,
self.uniform.transport.length)
}
match slice {
"" => None,
tcp if tcp.as_bytes()[0] == PsycTransport::PSYC_TRANSPORT_TCP as u8 =>
Some(PsycTransport::PSYC_TRANSPORT_TCP),
udp if udp.as_bytes()[0] == PsycTransport::PSYC_TRANSPORT_UDP as u8 =>
Some(PsycTransport::PSYC_TRANSPORT_UDP),
tls if tls.as_bytes()[0] == PsycTransport::PSYC_TRANSPORT_TLS as u8 =>
Some(PsycTransport::PSYC_TRANSPORT_TLS),
gnunet if gnunet.as_bytes()[0] == PsycTransport::PSYC_TRANSPORT_GNUNET as u8 =>
Some(PsycTransport::PSYC_TRANSPORT_GNUNET),
_ => None
}
}
pub fn resource(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.resource.data,
self.uniform.resource.length)
}
}
pub fn query(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.query.data, self.uniform.query.length)
}
}
pub fn full(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.full.data, self.uniform.full.length)
}
}
pub fn body(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.body.data, self.uniform.body.length)
}
}
pub fn root(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.root.data, self.uniform.root.length)
}
}
pub fn path(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.path.data, self.uniform.path.length)
}
}
/* private functions */
fn nick(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.nick.data, self.uniform.nick.length)
}
}
fn channel(&self) -> &'a str {
unsafe {
util::cstring_to_str(self.uniform.channel.data, self.uniform.channel.length)
}
}
}

88
rust/src/uniform_types.rs Normal file
View File

@ -0,0 +1,88 @@
use types::PsycString;
#[derive(Debug, PartialEq)]
pub enum PsycEntity<'a> {
Root,
Person {
name: &'a str,
channel: &'a str
},
Place {
name: &'a str,
channel: &'a str
},
Service {
name: &'a str,
channel: &'a str
},
Unknown {
object: &'a str,
channel: &'a str
}
}
#[derive(Clone, Copy)]
#[derive(Debug, PartialEq)]
#[repr(C)]
pub enum PsycScheme {
PSYC_SCHEME_PSYC = 0,
PSYC_SCHEME_IRC = 1,
PSYC_SCHEME_XMPP = 2,
PSYC_SCHEME_SIP = 3
}
#[repr(C)]
pub enum PsycEntityType {
PSYC_ENTITY_ROOT = 0,
PSYC_ENTITY_PERSON = '~' as _,
PSYC_ENTITY_PLACE = '@' as _,
PSYC_ENTITY_SERVICE = '$' as _
}
#[derive(Debug, PartialEq)]
#[repr(C)]
pub enum PsycTransport {
PSYC_TRANSPORT_TCP = 'c' as _,
PSYC_TRANSPORT_UDP = 'd' as _,
PSYC_TRANSPORT_TLS = 's' as _,
PSYC_TRANSPORT_GNUNET = 'g' as _,
}
#[derive(Debug, PartialEq)]
#[repr(C)]
pub enum UniformParseError {
PSYC_PARSE_UNIFORM_INVALID_SLASHES = -7,
PSYC_PARSE_UNIFORM_INVALID_CHANNEL = -6,
PSYC_PARSE_UNIFORM_INVALID_RESOURCE = -5,
PSYC_PARSE_UNIFORM_INVALID_TRANSPORT = -4,
PSYC_PARSE_UNIFORM_INVALID_PORT = -3,
PSYC_PARSE_UNIFORM_INVALID_HOST = -2,
PSYC_PARSE_UNIFORM_INVALID_SCHEME = -1,
}
#[derive(Debug)]
#[repr(C)]
pub struct PsycUniform {
pub valid: bool,
pub uniform_type: PsycScheme,
pub scheme: PsycString,
pub user: PsycString,
pub pass: PsycString,
pub host: PsycString,
pub port: PsycString,
pub transport: PsycString,
pub resource: PsycString,
pub query: PsycString,
pub channel: PsycString,
pub full: PsycString,
pub body: PsycString,
pub user_host: PsycString,
pub host_port: PsycString,
pub root: PsycString,
pub entity: PsycString,
pub slashes: PsycString,
pub slash: PsycString,
pub path: PsycString,
pub nick: PsycString
}

View File

@ -1,13 +1,23 @@
use packet_types::{RawPsycList, PsycRenderRC};
use std::slice;
use std::os::raw::c_char;
use std::str;
extern "C" {
fn psyc_render_list(list: *const RawPsycList, buffer: *mut c_char, buflen: usize) -> PsycRenderRC;
}
pub unsafe fn cstring_to_slice<'a>(cstring: *const c_char, length: usize) -> &'a[u8] {
slice::from_raw_parts(cstring as *const u8, length)
pub unsafe fn cstring_to_slice<'a>(cstring: *const c_char, length: usize)
-> &'a[u8] {
match cstring {
p if p.is_null() => &[],
_ => slice::from_raw_parts(cstring as *const u8, length)
}
}
pub unsafe fn cstring_to_str<'a>(cstring: *const c_char, length: usize)
-> &'a str {
str::from_utf8_unchecked(cstring_to_slice(cstring, length))
}
pub unsafe fn render_list(list: &RawPsycList) -> Vec<u8> {

69
rust/src/variable.rs Normal file
View File

@ -0,0 +1,69 @@
use types::PsycBool;
use variable_types::*;
use keyword::Keyword;
use std::os::raw::c_char;
extern "C" {
fn psyc_var_routing(name: *const c_char, len: usize) -> PsycRoutingVar;
fn psyc_var_type(name: *const c_char, len: usize) -> PsycType;
fn psyc_var_is_list(name: *const c_char, len: usize) -> PsycBool;
}
pub struct RoutingVariable<'a> {
pub variable: &'a [u8]
}
pub struct EntityVariable<'a> {
pub variable: &'a [u8]
}
impl<'a> Variable for RoutingVariable<'a> {
fn variable(&self) -> &[u8] {
self.variable
}
}
impl<'a> Variable for EntityVariable<'a> {
fn variable(&self) -> &[u8] {
self.variable
}
}
impl<'a> Keyword for RoutingVariable<'a> {
fn keyword(&self) -> &[u8] {
self.variable
}
}
impl<'a> Keyword for EntityVariable<'a> {
fn keyword(&self) -> &[u8] {
self.variable
}
}
impl<'a> RoutingVariable<'a> {
pub fn lookup(&self) -> PsycRoutingVar {
let variable_ptr = self.variable().as_ptr() as *const c_char;
unsafe {
psyc_var_routing(variable_ptr, self.variable().len())
}
}
}
pub trait Variable {
fn variable(&self) -> &[u8];
fn datatype(&self) -> PsycType {
let variable_ptr = self.variable().as_ptr() as *const c_char;
unsafe {
psyc_var_type(variable_ptr, self.variable().len())
}
}
fn is_list(&self) -> bool {
let variable_ptr = self.variable().as_ptr() as *const c_char;
unsafe {
psyc_var_is_list(variable_ptr, self.variable().len()).to_bool()
}
}
}

View File

@ -0,0 +1,39 @@
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycRoutingVar {
PSYC_RVAR_UNKNOWN,
PSYC_RVAR_AMOUNT_FRAGMENTS,
PSYC_RVAR_CONTEXT,
PSYC_RVAR_COUNTER,
PSYC_RVAR_FRAGMENT,
PSYC_RVAR_SOURCE,
PSYC_RVAR_SOURCE_RELAY,
PSYC_RVAR_TAG,
PSYC_RVAR_TAG_RELAY,
PSYC_RVAR_TARGET,
PSYC_RVAR_TARGET_RELAY,
PSYC_RVARS_NUM,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub enum PsycType {
PSYC_TYPE_UNKNOWN,
PSYC_TYPE_AMOUNT,
PSYC_TYPE_COLOR,
PSYC_TYPE_COUNTER,
PSYC_TYPE_DATE,
PSYC_TYPE_DEGREE,
PSYC_TYPE_DICT,
PSYC_TYPE_ENTITY,
PSYC_TYPE_FLAG,
PSYC_TYPE_LANGUAGE,
PSYC_TYPE_LIST,
PSYC_TYPE_NICK,
PSYC_TYPE_PAGE,
PSYC_TYPE_STRUCT,
PSYC_TYPE_TIME,
PSYC_TYPE_UNIFORM,
}

View File

@ -1,5 +1,5 @@
extern crate psyc;
use psyc::parser::*;
use psyc::*;
#[test]
fn test_parse() {

38
rust/tests/test_method.rs Normal file
View File

@ -0,0 +1,38 @@
extern crate psyc;
use psyc::*;
#[test]
fn test_method_matching() {
let m = Method {method: b"_notice_context_enter"};
assert!(m.inherits(b"_notice_context"));
assert!(m.matches(b"_context"));
}
#[test]
fn test_method_lookup() {
let m = Method {method: b"_notice_context_enter"};
let expected_flags = PSYC_METHOD_TEMPLATE |
PSYC_METHOD_VISIBLE |
PSYC_METHOD_LOGGABLE;
assert_eq!(m.lookup(),
MethodInfo {
lookup_result: PsycMethod::PSYC_MC_NOTICE_CONTEXT_ENTER,
family: PsycMethod::PSYC_MC_NOTICE,
flags: expected_flags
});
}
#[test]
fn test_lookup_empty() {
let m = Method {method: b""};
assert_eq!(m.lookup(),
MethodInfo {
lookup_result: PsycMethod::PSYC_MC_UNKNOWN,
family: PsycMethod::PSYC_MC_UNKNOWN,
flags: PsycMethodFlags::empty()
});
}

View File

@ -1,7 +1,6 @@
extern crate psyc;
use psyc::*;
use psyc::packet_types::*;
#[test]
fn test_create_render() {

View File

@ -1,7 +1,6 @@
extern crate psyc;
use psyc::*;
use psyc::packet_types::*;
#[test]
fn test_packet_id() {

View File

@ -1,5 +1,5 @@
extern crate psyc;
use psyc::parser::*;
use psyc::*;
#[test]
fn test_parse() {

View File

@ -0,0 +1,71 @@
extern crate psyc;
use psyc::*;
#[test]
fn test_person() {
let uniform = Uniform::from_bytes(b"psyc://ve.symlynx.com:4404/~lurchi");
let unwrapped = uniform.unwrap();
let entity = unwrapped.entity();
assert!(unwrapped.is_valid());
assert_eq!(unwrapped.full(), "psyc://ve.symlynx.com:4404/~lurchi");
assert_eq!(unwrapped.scheme(), PsycScheme::PSYC_SCHEME_PSYC);
assert_eq!(unwrapped.host(), "ve.symlynx.com");
assert_eq!(unwrapped.port(), Some(4404));
assert_eq!(unwrapped.resource(), "~lurchi");
assert_eq!(entity, PsycEntity::Person {name: "lurchi", channel: ""});
}
#[test]
fn test_place() {
let uniform = Uniform::from_bytes(b"psyc://ve.symlynx.com/@_strange_place");
let unwrapped = uniform.unwrap();
assert!(unwrapped.is_valid());
assert_eq!(unwrapped.resource(), "@_strange_place");
assert_eq!(unwrapped.entity(),
PsycEntity::Place {name: "_strange_place", channel: ""});
}
#[test]
fn test_host_only() {
let uniform = Uniform::from_bytes(b"psyc://ve.symlynx.com");
let unwrapped = uniform.unwrap();
assert!(unwrapped.is_valid());
assert_eq!(unwrapped.resource(), "");
assert_eq!(unwrapped.entity(), PsycEntity::Root);
}
#[test]
fn test_unknown_resource() {
let uniform = Uniform::from_bytes(b"psyc://ve.symlynx.com/%unknown#a");
let unwrapped = uniform.unwrap();
assert!(unwrapped.is_valid());
assert_eq!(unwrapped.entity(),
PsycEntity::Unknown {object: "%unknown", channel: "a"});
}
#[test]
fn test_channel() {
let uniform = Uniform::from_bytes(b"psyc://ve.symlynx.com:4404/~lurchi#test");
let unwrapped = uniform.unwrap();
assert_eq!(unwrapped.entity(), PsycEntity::Person {name: "lurchi", channel: "test"});
}
#[test]
fn test_empty() {
let uniform = Uniform::from_bytes(b"");
assert_eq!(uniform.unwrap_err(),
UniformParseError::PSYC_PARSE_UNIFORM_INVALID_SCHEME);
}

View File

@ -0,0 +1,44 @@
extern crate psyc;
use psyc::*;
#[test]
fn test_routing_variable() {
let r = RoutingVariable {variable: b"_amount_fragments"};
assert_eq!(r.lookup(), PsycRoutingVar::PSYC_RVAR_AMOUNT_FRAGMENTS);
assert_eq!(r.datatype(), PsycType::PSYC_TYPE_AMOUNT);
assert_eq!(r.is_list(), false);
}
#[test]
fn test_entity_variable() {
let e = EntityVariable {variable: b"_nick_family"};
assert_eq!(e.datatype(), PsycType::PSYC_TYPE_NICK);
assert!(! e.is_list());
}
#[test]
fn test_empty() {
let r = RoutingVariable {variable: b""};
assert_eq!(r.lookup(), PsycRoutingVar::PSYC_RVAR_UNKNOWN);
assert_eq!(r.datatype(), PsycType::PSYC_TYPE_UNKNOWN);
assert!(! r.is_list());
}
#[test]
fn test_inherits() {
let r = RoutingVariable {variable: b"_target_relay"};
assert!(r.inherits(b"_target"));
assert!(! r.inherits(b""));
}
#[test]
fn test_matches() {
let r = RoutingVariable {variable: b"_target_relay"};
assert!(r.matches(b"_relay"));
assert!(! r.matches(b""));
}

View File

@ -184,7 +184,7 @@ psyc_uniform_parse (PsycUniform *uni, const char *buffer, size_t length)
}
if (uni->resource.length)
uni->nick = PSYC_STRING(uni->resource.data + 1, uni->resource.length);
uni->nick = PSYC_STRING(uni->resource.data + 1, uni->resource.length - 1);
} else
return PSYC_PARSE_UNIFORM_INVALID_SCHEME;