Browse Source

Improve docs and error handling

master
Alex Williams 7 months ago
parent
commit
18f6c4418a
Signed by: aw GPG Key ID: 19EE4AAA361A7E2C
8 changed files with 1129 additions and 231 deletions
  1. +1
    -0
      .gitignore
  2. +0
    -5
      Cargo.lock
  3. +50
    -0
      src/bin/validate.rs
  4. +535
    -123
      src/decode.rs
  5. +422
    -90
      src/encode.rs
  6. +88
    -0
      src/errors.rs
  7. +33
    -13
      src/lib.rs
  8. BIN
      testdata/boawp.bin

+ 1
- 0
.gitignore View File

@ -1,3 +1,4 @@
target/
rust-1*
rustc-1*
Cargo.lock

+ 0
- 5
Cargo.lock View File

@ -1,5 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "boawp"
version = "0.1.0"

+ 50
- 0
src/bin/validate.rs View File

@ -0,0 +1,50 @@
/*!
Validates if a file's contents contain valid _BOAWP_ objects.
This tool can be used to validate if your **encoder** implementation is correctly encoding _BOAWP Objects_.
To validate your **decoder** implementation, ensure it can successfully decode the `boawp.bin` file in the `testdata/` directory.
# Usage example
```sh
$ cargo run --quiet testdata/boawp.bin; echo $?
Decoded from testdata/boawp.bin:
[String("BOA01\n"), Binary([0]), String("!"), Integer(Two(49)), Integer(Four(0)), String("a"), Null, String("b"), Boolean(true), String("c"), Boolean(false), String("d"), Integer(Two(2571)), String("e"), String("ABCD"), String("f"), Binary([1, 2, 3, 4]), String("g"), Map([(Boolean(true), Boolean(false))])]
0
```
In the example above, the `boawp.bin` file was decoded successfully by the validator. The exit code will be 0 on success.
*/
use boawp::*;
use std::{io::Read, env, fs::File, io::BufReader};
type GenError = Box<dyn std::error::Error>;
type GenResult<T> = Result<T, GenError>;
fn validate<R: Read>(reader: &mut R) -> GenResult<Vec<DataType>> {
// read the entire file
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer)?;
let mut objects = Vec::new();
// read each object
while buffer.len() > 0 {
let obj = decode::read_object(&mut buffer)?;
objects.push(obj);
}
Ok(objects)
}
fn main() -> GenResult<()> {
let args: Vec<String> = env::args().collect();
let file = File::open(&args[1])?;
let mut reader = BufReader::new(file);
let decoded = validate(&mut reader)?;
println!("Decoded from {}: \n{:?}", &args[1], &decoded);
Ok(())
}

+ 535
- 123
src/decode.rs View File

@ -1,22 +1,106 @@
use std::{convert::TryInto, io::Error, io::ErrorKind};
use std::convert::TryInto;
use super::*;
pub fn read_null(buf: DataBuffer) -> ResultDataType {
read_type_length(buf, 'n')?;
Ok(DataType::Null)
}
// Macros
macro_rules! create_read_bool {
($fname:ident, $i:expr, $x:expr) => {
/**
Read an object of type `null`, `true`, or `false`
pub fn read_true(buf: DataBuffer) -> ResultDataType {
read_type_length(buf, 't')?;
Ok(DataType::Boolean(true))
}
The length of this type must always be 0.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| null | 110 | 0x6E | "n" | |
| true | 116 | 0x74 | "t" | |
| false | 102 | 0x66 | "f" | |
# Examples
```rust
let mut buf: Vec<u8> = vec![b'n', 0, 0x74, 0, 102, 0];
let value = boawp::decode::read_null(&mut buf).unwrap();
assert_eq!(value, boawp::DataType::Null);
// the first object was removed from the buffer, see below:
assert_eq!(buf, vec![116, 0, 0x66, 0]);
let value = boawp::decode::read_true(&mut buf).unwrap();
assert_eq!(value, boawp::DataType::Boolean(true));
let value = boawp::decode::read_false(&mut buf).unwrap();
assert_eq!(value, boawp::DataType::Boolean(false));
```
# Invalid usage
pub fn read_false(buf: DataBuffer) -> ResultDataType {
read_type_length(buf, 'f')?;
Ok(DataType::Boolean(false))
```rust
// this type must have a length 0
let mut buf = vec![b'n', 1, 65];
let value = boawp::decode::read_null(&mut buf);
assert!(value.is_err());
```
*/
pub fn $fname(buf: DataBuffer) -> ResultDataType {
read_type_length(buf, $i)?;
Ok($x)
}
};
}
create_read_bool!(read_null, 'n', DataType::Null);
create_read_bool!(read_true, 't', DataType::Boolean(true));
create_read_bool!(read_false, 'f', DataType::Boolean(false));
// Public functions
/**
Read an object of type `integer`
The length of this `integer` can be any length between 0 and 255 inclusively.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,DataType,Integer};
let mut buf: Vec<u8> = vec![b'i', 0, b'i', 2, 255, 255, b'i', 4, 0, 0, 0, 42];
let value = decode::read_integer(&mut buf).unwrap();
assert_eq!(value, DataType::Integer(Integer::Zero()));
// the first object was removed from the buffer, see below:
assert_eq!(buf, vec![b'i', 2, 255, 255, b'i', 4, 0, 0, 0, 42]);
let value = decode::read_integer(&mut buf).unwrap();
assert_eq!(value, DataType::Integer(Integer::Two(-1)));
let value = decode::read_integer(&mut buf).unwrap();
assert_eq!(value, DataType::Integer(Integer::Four(42)));
// any integer length that isn't 0, 1, 2, 4, 8, or 16 will be parsed as BigNum
let mut buf = vec![b'i', 7, 1, 2, 3, 4, 5, 6, 7];
let value = decode::read_integer(&mut buf).unwrap();
assert_eq!(value, DataType::Integer(Integer::BigNum(vec![1, 2, 3, 4, 5, 6, 7])));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 1 needs a buffer of at least 3 elements
let mut buf = vec![b'i', 1];
let value = decode::read_integer(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_integer(buf: DataBuffer) -> ResultDataType {
let tl = read_type_length(buf, 'i')?;
let buffer: Vec<u8> = buf.drain(..tl.dlength as usize).collect();
@ -32,6 +116,48 @@ pub fn read_integer(buf: DataBuffer) -> ResultDataType {
Ok(DataType::Integer(int))
}
/**
Read an object of type `string`
The length of this `string` can be any length between 0 and 255 inclusively.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| string | 115 | 0x73 | "s" | UTF-8 string |
# Examples
```rust
use boawp::{decode,DataType};
let mut buf: Vec<u8> = vec![b's', 0, b's', 4, 65, 66, 67, 68];
let value = decode::read_string(&mut buf).unwrap();
assert_eq!(value, DataType::String("".to_string()));
// the first object was removed from the buffer, see below:
assert_eq!(buf, vec![b's', 4, 65, 66, 67, 68]);
let value = decode::read_string(&mut buf).unwrap();
assert_eq!(value, DataType::String(String::from("ABCD")));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 1 needs a buffer of at least 3 elements
let mut buf = vec![b's', 1];
let value = decode::read_string(&mut buf);
assert!(value.is_err());
// this type requires a valid UTF-8 string
let mut buf = vec![b's', 2, 255, 255];
let value = decode::read_string(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_string(buf: DataBuffer) -> ResultDataType {
let tl = read_type_length(buf, 's')?;
let buffer: Vec<u8> = buf.drain(..tl.dlength as usize).collect();
@ -40,95 +166,406 @@ pub fn read_string(buf: DataBuffer) -> ResultDataType {
if let Ok(x) = res {
Ok(DataType::String(x))
} else {
Err(Error::new(ErrorKind::InvalidData, "Invalid UTF-8 string"))
Err(BoawpDecodeError::InvalidUTF8)
}
}
/**
Read an object of type `binary`
The length of this `binary` vector can be any length between 0 and 255 inclusively.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| binary | 98 | 0x62 | "b" | sequence of raw octets (bytes) |
# Examples
```rust
use boawp::{decode,DataType};
let mut buf: Vec<u8> = vec![b'b', 0, b'b', 4, 65, 66, 67, 68];
let value = decode::read_binary(&mut buf).unwrap();
assert_eq!(value, DataType::Binary(vec![]));
// the first object was removed from the buffer, see below:
assert_eq!(buf, vec![b'b', 4, 65, 66, 67, 68]);
let value = decode::read_binary(&mut buf).unwrap();
assert_eq!(value, DataType::Binary(vec![65, 66, 67, 68]));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 1 needs a buffer of at least 3 elements
let mut buf = vec![b's', 1];
let value = decode::read_binary(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_binary(buf: DataBuffer) -> ResultDataType {
let tl = read_type_length(buf, 'b')?;
let buffer: Vec<u8> = buf.drain(..tl.dlength as usize).collect();
Ok(DataType::Binary(buffer))
}
/**
Read an object of type `map`
The length of this `map` can be any length between 0 and 255 inclusively.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| map | 109 | 0x6D | "m" | array of key/value pairs (tuples) |
# Examples
```rust
use boawp::{decode,DataType};
let mut buf: Vec<u8> = vec![b'm', 4, b't', 0, b'f', 0];
let value = decode::read_map(&mut buf).unwrap();
assert_eq!(value, DataType::Map(vec![(DataType::Boolean(true), DataType::Boolean(false))]));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 3 needs a buffer of at least 4 elements
let mut buf = vec![b'm', 3, 116, 0, 102];
let value = decode::read_map(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_map(buf: DataBuffer) -> ResultDataType {
let tl = read_type_length(buf, 'm')?;
let mut dmap: DataMap = Vec::with_capacity(tl.dlength as usize);
while buf.len() > 3 {
let dkey = read_value(buf)?;
let dvalue = read_value(buf)?;
let dkey = read_object(buf)?;
let dvalue = read_object(buf)?;
dmap.push((dkey, dvalue));
}
Ok(DataType::Map(dmap))
}
/**
Read an 8-bit object of type `integer`
The length of this `i8` integer must be exactly 1.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,Integer};
let mut buf: Vec<u8> = vec![b'i', 1, 42];
let value = decode::read_i8(&mut buf).unwrap();
assert_eq!(value, Integer::One(42));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 1 needs a buffer of at least 3 elements
let mut buf = vec![b'i', 1];
let value = decode::read_i8(&mut buf);
assert!(value.is_err());
// this type's length must be 1
let mut buf = vec![b'i', 2, 255, 255];
let value = decode::read_i8(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_i8(buf: DataBuffer) -> ResultInteger {
let int = read_integer(buf)?;
match int {
DataType::Integer(Integer::One(x)) => Ok(Integer::One(x)),
_ => Err(Error::new(
ErrorKind::InvalidData,
"Invalid integer length, should be 1",
)),
_ => Err(BoawpDecodeError::InvalidIntegerLength(1.to_string())),
}
}
/**
Read a 16-bit object of type `integer`
The length of this `i16` integer must be exactly 2.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,Integer};
let mut buf: Vec<u8> = vec![b'i', 2, 255, 255];
let value = decode::read_i16(&mut buf).unwrap();
assert_eq!(value, Integer::Two(-1));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 2 needs a buffer of at least 4 elements
let mut buf = vec![b'i', 2, 255];
let value = decode::read_i16(&mut buf);
assert!(value.is_err());
// this type's length must be 2
let mut buf = vec![b'i', 3, 255, 255, 255];
let value = decode::read_i16(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_i16(buf: DataBuffer) -> ResultInteger {
let int = read_integer(buf)?;
match int {
DataType::Integer(Integer::Two(x)) => Ok(Integer::Two(x)),
_ => Err(Error::new(
ErrorKind::InvalidData,
"Invalid integer length, should be 2",
)),
_ => Err(BoawpDecodeError::InvalidIntegerLength(2.to_string())),
}
}
/**
Read a 32-bit object of type `integer`
The length of this `i32` integer must be exactly 4.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,Integer};
let mut buf: Vec<u8> = vec![b'i', 4, 127, 255, 255, 255];
let value = decode::read_i32(&mut buf).unwrap();
assert_eq!(value, Integer::Four(2147483647));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 4 needs a buffer of at least 6 elements
let mut buf = vec![b'i', 4, 255, 255, 255];
let value = decode::read_i32(&mut buf);
assert!(value.is_err());
// this type's length must be 4
let mut buf = vec![b'i', 5, 255, 255, 255, 255, 255];
let value = decode::read_i32(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_i32(buf: DataBuffer) -> ResultInteger {
let int = read_integer(buf)?;
match int {
DataType::Integer(Integer::Four(x)) => Ok(Integer::Four(x)),
_ => Err(Error::new(
ErrorKind::InvalidData,
"Invalid integer length, should be 4",
)),
_ => Err(BoawpDecodeError::InvalidIntegerLength(4.to_string())),
}
}
/**
Read a 64-bit object of type `integer`
The length of this `i64` integer must be exactly 8.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,Integer};
let mut buf: Vec<u8> = vec![b'i', 8, 127, 255, 255, 255, 255, 255, 255, 255];
let value = decode::read_i64(&mut buf).unwrap();
assert_eq!(value, Integer::Eight(9223372036854775807));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 8 needs a buffer of at least 10 elements
let mut buf = vec![b'i', 8, 255, 255, 255, 255, 255, 255, 255];
let value = decode::read_i64(&mut buf);
assert!(value.is_err());
// this type's length must be 8
let mut buf = vec![b'i', 5, 255, 255, 255, 255, 255];
let value = decode::read_i64(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_i64(buf: DataBuffer) -> ResultInteger {
let int = read_integer(buf)?;
match int {
DataType::Integer(Integer::Eight(x)) => Ok(Integer::Eight(x)),
_ => Err(Error::new(
ErrorKind::InvalidData,
"Invalid integer length, should be 8",
)),
_ => Err(BoawpDecodeError::InvalidIntegerLength(8.to_string())),
}
}
/**
Read a 128-bit object of type `integer`
The length of this `i128` integer must be exactly 16.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,Integer};
let mut buf: Vec<u8> = vec![b'i', 16, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255];
let value = decode::read_i128(&mut buf).unwrap();
assert_eq!(value, Integer::Sixteen(170141183460469231731687303715884105727));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 16 needs a buffer of at least 18 elements
let mut buf = vec![b'i', 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255];
let value = decode::read_i128(&mut buf);
assert!(value.is_err());
// this type's length must be 16
let mut buf = vec![b'i', 5, 255, 255, 255, 255, 255];
let value = decode::read_i128(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_i128(buf: DataBuffer) -> ResultInteger {
let int = read_integer(buf)?;
match int {
DataType::Integer(Integer::Sixteen(x)) => Ok(Integer::Sixteen(x)),
_ => Err(Error::new(
ErrorKind::InvalidData,
"Invalid integer length, should be 16",
)),
_ => Err(BoawpDecodeError::InvalidIntegerLength(16.to_string())),
}
}
/**
Read a "BigNum" object of type `integer`
The length of this `BigNum` integer can be any length between 0 and 255 inclusively.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{decode,Integer};
let mut buf: Vec<u8> = vec![b'i', 7, 127, 255, 255, 255, 255, 255, 255];
let value = decode::read_bignum(&mut buf).unwrap();
assert_eq!(value, Integer::BigNum(vec![127, 255, 255, 255, 255, 255, 255]));
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 1 needs a buffer of at least 3 elements
let mut buf = vec![b'i', 1];
let value = decode::read_bignum(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_bignum(buf: DataBuffer) -> ResultInteger {
let int = read_integer(buf)?;
match int {
DataType::Integer(Integer::BigNum(x)) => Ok(Integer::BigNum(x)),
_ => Err(Error::new(
ErrorKind::InvalidData,
"Invalid integer length, should be BigNum",
)),
_ => Err(BoawpDecodeError::InvalidIntegerLength("BigNum".to_string())),
}
}
pub fn read_value(buf: DataBuffer) -> ResultDataType {
/**
Read an object of any type
The length of this object can be any length between 0 and 255 inclusively.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| null | 110 | 0x6E | "n" | |
| true | 116 | 0x74 | "t" | |
| false | 102 | 0x66 | "f" | |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
| string | 115 | 0x73 | "s" | UTF-8 string |
| binary | 98 | 0x62 | "b" | sequence of raw octets (bytes) |
| map | 109 | 0x6D | "m" | array of key/value pairs (tuples) |
# Examples
```rust
use boawp::{decode,DataType,Integer};
let mut buf: Vec<u8> = vec![b'i', 4, 255, 255, 255, 127, b'n', 0];
let value = decode::read_object(&mut buf).unwrap();
assert_eq!(value, DataType::Integer(Integer::Four(-129)));
let value = decode::read_object(&mut buf).unwrap();
assert_eq!(value, DataType::Null);
```
# Invalid usage
```rust
use boawp::decode;
// this type with a length 1 needs a buffer of at least 3 elements
let mut buf = vec![b'i', 1];
let value = decode::read_object(&mut buf);
assert!(value.is_err());
```
*/
pub fn read_object(buf: DataBuffer) -> ResultDataType {
if let true = buf.len() < 2 {
return Err(Error::new(
ErrorKind::InvalidData,
"Data buffer too small, should be at least 2",
));
return Err(BoawpDecodeError::DataBufferTooSmall(2.to_string()));
}
let value = match buf[0] as char {
'n' => read_null(buf)?,
@ -138,55 +575,37 @@ pub fn read_value(buf: DataBuffer) -> ResultDataType {
's' => read_string(buf)?,
'b' => read_binary(buf)?,
'm' => read_map(buf)?,
_ => return Err(Error::new(ErrorKind::InvalidData, "Unknown data type")),
_ => return Err(BoawpDecodeError::UnknownDataType),
};
Ok(value)
}
fn read_type_length(buf: DataBuffer, xtype: char) -> Result<DataTypeLength, Error> {
// Private functions
fn read_type_length(buf: DataBuffer, xtype: char) -> Result<DataTypeLength, BoawpDecodeError> {
if let true = buf.len() < 2 {
return Err(Error::new(
ErrorKind::InvalidData,
"Data buffer too small, should be at least 2",
));
return Err(BoawpDecodeError::DataBufferTooSmall(2.to_string()));
}
let dtype = buf.remove(0) as char;
let dlength = buf.remove(0) as u8;
match dlength {
x if buf.len() < x as usize => {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"Data buffer too small for type {}, should be at least {}",
dtype, dlength
),
))
}
_ if dtype != xtype => {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid data type, should be {}", xtype),
return Err(BoawpDecodeError::DataBufferTooSmallForType(
dtype.to_string(),
dlength.to_string(),
))
}
_ if dtype != xtype => return Err(BoawpDecodeError::InvalidDataType(xtype.to_string())),
_ => (),
}
match dtype {
'n' | 't' | 'f' => {
if let true = dlength != 0 {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid data length for type {}", dtype),
));
return Err(BoawpDecodeError::InvalidDataLength(dtype.to_string()));
}
}
'm' => match dlength {
1 | 2 | 3 => {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid data length for type {}", dtype),
))
}
1 | 2 | 3 => return Err(BoawpDecodeError::InvalidDataLength(dtype.to_string())),
_ => {}
},
_ => {}
@ -195,8 +614,7 @@ fn read_type_length(buf: DataBuffer, xtype: char) -> Result<DataTypeLength, Erro
Ok(res)
}
// TESTS
// Tests
#[cfg(test)]
mod tests {
use super::*;
@ -218,7 +636,7 @@ mod tests {
let value = read_null(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be n"
);
@ -226,7 +644,7 @@ mod tests {
let value = read_null(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data length for type n"
);
@ -234,7 +652,7 @@ mod tests {
let value = read_null(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type n, should be at least 1"
);
@ -242,7 +660,7 @@ mod tests {
let value = read_null(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
}
@ -264,7 +682,7 @@ mod tests {
let value = read_true(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be t"
);
@ -272,7 +690,7 @@ mod tests {
let value = read_true(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data length for type t"
);
@ -280,7 +698,7 @@ mod tests {
let value = read_true(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type t, should be at least 1"
);
@ -288,7 +706,7 @@ mod tests {
let value = read_true(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
}
@ -310,7 +728,7 @@ mod tests {
let value = read_false(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be f"
);
@ -318,7 +736,7 @@ mod tests {
let value = read_false(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data length for type f"
);
@ -326,7 +744,7 @@ mod tests {
let value = read_false(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type f, should be at least 1"
);
@ -334,7 +752,7 @@ mod tests {
let value = read_false(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
}
@ -394,7 +812,7 @@ mod tests {
let value = read_integer(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be i"
);
@ -402,7 +820,7 @@ mod tests {
let value = read_integer(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type i, should be at least 1"
);
@ -410,7 +828,7 @@ mod tests {
let value = read_integer(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
}
@ -432,7 +850,7 @@ mod tests {
let value = read_string(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be s"
);
@ -440,7 +858,7 @@ mod tests {
let value = read_string(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type s, should be at least 1"
);
@ -448,17 +866,14 @@ mod tests {
let value = read_string(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
let mut x = vec![115, 2, 255, 255];
let value = read_string(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
"Invalid UTF-8 string"
);
assert_eq!(value.unwrap_err().to_string(), "Invalid UTF-8 string");
}
#[test]
fn _read_binary() {
@ -478,7 +893,7 @@ mod tests {
let value = read_binary(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be b"
);
@ -486,7 +901,7 @@ mod tests {
let value = read_binary(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type b, should be at least 1"
);
@ -494,7 +909,7 @@ mod tests {
let value = read_binary(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
}
@ -541,7 +956,7 @@ mod tests {
let value = read_map(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data type, should be m"
);
@ -549,7 +964,7 @@ mod tests {
let value = read_map(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small for type m, should be at least 1"
);
@ -557,7 +972,7 @@ mod tests {
let value = read_map(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid data length for type m"
);
@ -565,7 +980,7 @@ mod tests {
let value = read_map(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Data buffer too small, should be at least 2"
);
}
@ -583,7 +998,7 @@ mod tests {
let value = read_i8(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid integer length, should be 1"
);
}
@ -597,7 +1012,7 @@ mod tests {
let value = read_i16(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid integer length, should be 2"
);
}
@ -611,7 +1026,7 @@ mod tests {
let value = read_i32(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid integer length, should be 4"
);
}
@ -625,7 +1040,7 @@ mod tests {
let value = read_i64(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid integer length, should be 8"
);
}
@ -648,7 +1063,7 @@ mod tests {
let value = read_i128(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid integer length, should be 16"
);
}
@ -662,48 +1077,45 @@ mod tests {
let value = read_bignum(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
value.unwrap_err().to_string(),
"Invalid integer length, should be BigNum"
);
}
#[test]
fn _read_value() {
fn _read_object() {
let mut x = vec![110, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::Null);
let mut x = vec![116, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::Boolean(true));
let mut x = vec![102, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::Boolean(false));
let mut x = vec![105, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::Integer(Integer::Zero()));
let mut x = vec![115, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::String("".to_string()));
let mut x = vec![98, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::Binary(vec![]));
let mut x = vec![109, 0];
let value = read_value(&mut x).unwrap();
let value = read_object(&mut x).unwrap();
assert_eq!(value, DataType::Map(vec![]));
}
#[test]
fn _read_value_error() {
fn _read_object_error() {
let mut x = vec![120, 110, 0];
let value = read_value(&mut x);
let value = read_object(&mut x);
assert!(value.is_err());
assert_eq!(
value.unwrap_err().into_inner().unwrap().to_string(),
"Unknown data type"
);
assert_eq!(value.unwrap_err().to_string(), "Unknown data type");
}
}

+ 422
- 90
src/encode.rs View File

@ -1,25 +1,158 @@
use std::{io::Error, io::ErrorKind};
use super::*;
pub fn write_null() -> ResultBuffer<'static> {
let res: Vec<u8> = vec!['n' as u8, 0];
Ok(res)
}
// Macros
macro_rules! create_write_bool {
($fname:ident, $i:expr) => {
/**
Write an object of type `null`, `true`, or `false`
pub fn write_true() -> ResultBuffer<'static> {
let res: Vec<u8> = vec!['t' as u8, 0];
Ok(res)
This will return a `Vec<u8>` which contains 2 elements; the `type` and `0`.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| null | 110 | 0x6E | "n" | |
| true | 116 | 0x74 | "t" | |
| false | 102 | 0x66 | "f" | |
# Examples
```rust
let result = boawp::encode::write_null().unwrap();
assert_eq!(result, vec![b'n', 0]);
let result = boawp::encode::write_true().unwrap();
assert_eq!(result, vec![0x74, 0]);
let result = boawp::encode::write_false().unwrap();
assert_eq!(result, vec![102, 0]);
```
*/
pub fn $fname() -> ResultBuffer<'static> {
let res: Vec<u8> = vec![$i as u8, 0];
Ok(res)
}
};
}
pub fn write_false() -> ResultBuffer<'static> {
let res: Vec<u8> = vec!['f' as u8, 0];
Ok(res)
macro_rules! create_write_integer {
($fname:ident, $i:ty, $x:expr) => {
/**
Write a strict `integer` type object
This will return a `Vec<u8>` which contains 3 to 257 elements; `0x69`, the length, and the integer in octets (bytes).
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
let result = boawp::encode::write_i8(10).unwrap();
assert_eq!(result, vec![b'i', 1, 10]);
let result = boawp::encode::write_i16(-1).unwrap();
assert_eq!(result, vec![0x69, 2, 255, 255]);
let result = boawp::encode::write_bignum(vec![255, 255, 255]).unwrap();
assert_eq!(result, vec![105, 3, 255, 255, 255]);
```
*/
pub fn $fname(z: $i) -> ResultBuffer<'static> {
let int = write_integer(&$x(z))?;
Ok(int)
}
};
($fname:ident, $x:expr) => {
/**
Write a zero-length object of type `integer`
This will return a `Vec<u8>` which contains 2 elements; `0x69` and `0`.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
let result = boawp::encode::write_zero().unwrap();
assert_eq!(result, vec![b'i', 0]);
*/
pub fn $fname() -> ResultBuffer<'static> {
let int = write_integer(&$x())?;
Ok(int)
}
};
}
create_write_bool!(write_null, 'n');
create_write_bool!(write_true, 't');
create_write_bool!(write_false, 'f');
create_write_integer!(write_zero, Integer::Zero);
create_write_integer!(write_i8, i8, Integer::One);
create_write_integer!(write_i16, i16, Integer::Two);
create_write_integer!(write_i32, i32, Integer::Four);
create_write_integer!(write_i64, i64, Integer::Eight);
create_write_integer!(write_i128, i128, Integer::Sixteen);
create_write_integer!(write_bignum, Vec<u8>, Integer::BigNum);
// Public functions
/**
Write an object of type `integer`
This will return a `Vec<u8>` which contains 2 to 257 elements; `0x69`, the length, and the integer in octets (bytes).
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::{encode,Integer};
let mut buf = Integer::Zero();
let value = encode::write_integer(&mut buf).unwrap();
assert_eq!(value, vec![b'i', 0]);
buf = Integer::Two(-1);
let value = encode::write_integer(&mut buf).unwrap();
assert_eq!(value, vec![b'i', 2, 255, 255]);
buf = Integer::Four(42);
let value = encode::write_integer(&mut buf).unwrap();
assert_eq!(value, vec![b'i', 4, 0, 0, 0, 42]);
// any integer length that isn't 0, 1, 2, 4, 8, or 16 will be parsed as BigNum
buf = Integer::BigNum(vec![1, 2, 3, 4, 5, 6, 7]);
let value = encode::write_integer(&mut buf).unwrap();
assert_eq!(value, vec![b'i', 7, 1, 2, 3, 4, 5, 6, 7]);
```
# Invalid usage
```rust
use boawp::{encode,Integer};
// this type can have at most 255 elements
let mut buf = Integer::BigNum(vec![0; 256]);
let value = encode::write_integer(&mut buf);
assert!(value.is_err());
```
*/
pub fn write_integer(z: &Integer) -> ResultBuffer {
let mut res: Vec<u8> = vec!['i' as u8];
let mut int = match z {
let mut int: Vec<u8> = match z {
Integer::Zero() => vec![],
Integer::One(i) => i.to_be_bytes().to_vec(),
Integer::Two(i) => i.to_be_bytes().to_vec(),
@ -28,12 +161,44 @@ pub fn write_integer(z: &Integer) -> ResultBuffer {
Integer::Sixteen(i) => i.to_be_bytes().to_vec(),
Integer::BigNum(i) => i.to_vec(),
};
if let true = int.len() > 255 { return Err(Error::new(ErrorKind::InvalidData, "Invalid integer value length, max 255")) }
if let true = int.len() > 255 {
return Err(BoawpEncodeError::InvalidLength("integer value".to_string()));
}
let mut res: Vec<u8> = Vec::with_capacity(2 + int.len());
res.push('i' as u8);
res.push(int.len() as u8);
res.append(&mut int);
Ok(res)
}
/**
Write an i128 (signed integer) of type `integer` by guessing its size
This will return a `Vec<u8>` which contains 3 to 18 elements; `0x69`, the length, and the integer in octets (bytes).
This function should be used when the size of the integer is not known. It will automatically choose the smallest possible signed integer between i8, i16, i32, i64, and i128.
# Table of types
| Name | Decimal | Hex | UTF-8 | Description |
| :---- | :----: | :----: | :----: | :---- |
| integer | 105 | 0x69 | "i" | signed big-endian integer (2s complement) |
# Examples
```rust
use boawp::encode;
let value = encode::write_sint(127).unwrap();
assert_eq!(value, vec![b'i', 1, 127]);
let value = encode::write_sint(256).unwrap();
assert_eq!(value, vec![b'i', 2, 1, 0]);
let value = encode::write_sint(-32999).unwrap();
assert_eq!(value, vec![b'i', 4, 255, 255, 127, 25]);
```
*/
pub fn write_sint(z: i128) -> ResultBuffer<'static> {
let int = match z {
i if i >= i8::MIN.into() && i <= i8::MAX.into() => write_i8(i as i8)?,
@ -41,98 +206,231 @@ pub fn write_sint(z: i128) -> ResultBuffer<'static> {
i if i >= i32::MIN.into() && i <= i32::MAX.into() => write_i32(z as i32)?,
i if i >= i64::MIN.into() && i <= i64::MAX.into() => write_i64(z as i64)?,
i if i >= i128::MIN.into() && i <= i128::MAX.into() => write_i128(z)?,
_ => return Err(Error::new(ErrorKind::InvalidData, "Invalid signed integer value length"))
_ => return Err(BoawpEncodeError::InvalidSignedLength),
};
Ok(int)
}
pub fn write_string(z: &String) -> ResultBuffer {
if let true = z.len() > 255 { return Err(Error::new(ErrorKind::InvalidData, "Invalid string value length, max 255")) }