Added a few tests and got them working. Added feature "trace".
This commit is contained in:
parent
2349c9e087
commit
40366a6afe
3 changed files with 109 additions and 14 deletions
|
@ -11,4 +11,11 @@ repository = "https://github.com/fluffysquirrels/framed"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cobs = "^0.1.2"
|
cobs = "^0.1.2"
|
||||||
error-chain = "^0.11.0"
|
error-chain = "^0.11.0"
|
||||||
|
ref_slice = "^1.1.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default_features = []
|
||||||
|
|
||||||
|
# Enable to print all data to stdout for testing.
|
||||||
|
trace = []
|
|
@ -12,5 +12,9 @@ error_chain! {
|
||||||
description("COBS decode failed"),
|
description("COBS decode failed"),
|
||||||
display("COBS decode failed"),
|
display("COBS decode failed"),
|
||||||
}
|
}
|
||||||
|
EofDuringFrame {
|
||||||
|
description("end of file during a frame"),
|
||||||
|
display("end of file during a frame"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
110
src/lib.rs
110
src/lib.rs
|
@ -8,8 +8,19 @@
|
||||||
//!
|
//!
|
||||||
//! [dll]: https://en.wikipedia.org/wiki/Data_link_layer
|
//! [dll]: https://en.wikipedia.org/wiki/Data_link_layer
|
||||||
//! [pl]: https://en.wikipedia.org/wiki/Physical_layer
|
//! [pl]: https://en.wikipedia.org/wiki/Physical_layer
|
||||||
|
//!
|
||||||
// TODO: Make this work when no_std.
|
//! Currently the encoding is: the frame [COBS]-encoded
|
||||||
|
//! to remove bytes equal to zero, then a terminating zero byte.
|
||||||
|
//! [COBS]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
|
||||||
|
//!
|
||||||
|
//! ## Cargo feature flags
|
||||||
|
//! `trace`: Enable to print all data to stdout for testing.
|
||||||
|
//!
|
||||||
|
//! ## TODO
|
||||||
|
//! * Start frame with a zero as well, then we detect partial frames.
|
||||||
|
//! * Add a length field to the beginning of the frame and a
|
||||||
|
//! checksum to the end. Perhaps make them optional.
|
||||||
|
//! * Support no_std.
|
||||||
|
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![feature(conservative_impl_trait)]
|
#![feature(conservative_impl_trait)]
|
||||||
|
@ -17,12 +28,16 @@
|
||||||
extern crate cobs;
|
extern crate cobs;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
|
extern crate ref_slice;
|
||||||
|
|
||||||
pub mod channel;
|
pub mod channel;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
use error::{Error, ErrorKind, Result};
|
use error::{Error, ErrorKind, Result};
|
||||||
use std::io::{Read, Write};
|
use ref_slice::ref_slice_mut;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
|
const FRAME_END: u8 = 0;
|
||||||
|
|
||||||
/// Sends frames over an underlying `io::Write` instance.
|
/// Sends frames over an underlying `io::Write` instance.
|
||||||
pub struct Sender<W: Write> {
|
pub struct Sender<W: Write> {
|
||||||
|
@ -37,7 +52,11 @@ impl<W: Write> Sender<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&mut self, f: &[u8]) -> Result<()> {
|
pub fn send(&mut self, f: &[u8]) -> Result<()> {
|
||||||
let code = cobs::encode_vec(f);
|
let mut code = cobs::encode_vec(f);
|
||||||
|
code.push(FRAME_END);
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed: Sending code = {:?}", code);
|
||||||
|
}
|
||||||
self.w.write(&code)?;
|
self.w.write(&code)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -45,6 +64,7 @@ impl<W: Write> Sender<W> {
|
||||||
|
|
||||||
/// Receives frames from an underlying `io::Read` instance.
|
/// Receives frames from an underlying `io::Read` instance.
|
||||||
pub struct Receiver<R: Read> {
|
pub struct Receiver<R: Read> {
|
||||||
|
/// The underlying reader
|
||||||
r: R,
|
r: R,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,9 +76,35 @@ impl<R: Read> Receiver<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv(&mut self) -> Result<Vec<u8>> {
|
pub fn recv(&mut self) -> Result<Vec<u8>> {
|
||||||
let mut code = Vec::new();
|
let mut next_frame = Vec::new();
|
||||||
self.r.read_to_end(&mut code)?;
|
|
||||||
cobs::decode_vec(&code)
|
let mut b = 0u8;
|
||||||
|
loop {
|
||||||
|
let res = self.r.read(ref_slice_mut(&mut b));
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed: Read result = {:?}", res);
|
||||||
|
}
|
||||||
|
match res {
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof =>
|
||||||
|
return Err(Error::from(ErrorKind::EofDuringFrame)),
|
||||||
|
Ok(0) =>
|
||||||
|
return Err(Error::from(ErrorKind::EofDuringFrame)),
|
||||||
|
Err(e) => return Err(Error::from(e)),
|
||||||
|
Ok(_) => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "trace")] {
|
||||||
|
println!("framed: Read byte = {}", b);
|
||||||
|
}
|
||||||
|
if b == FRAME_END {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
next_frame.push(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(b == FRAME_END);
|
||||||
|
|
||||||
|
cobs::decode_vec(&next_frame)
|
||||||
.map_err(|_| Error::from(ErrorKind::CobsDecodeFailed))
|
.map_err(|_| Error::from(ErrorKind::CobsDecodeFailed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +112,7 @@ impl<R: Read> Receiver<R> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use channel::Channel;
|
use channel::Channel;
|
||||||
|
use error::{Error, ErrorKind};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use super::{Receiver, Sender};
|
use super::{Receiver, Sender};
|
||||||
|
|
||||||
|
@ -78,13 +125,50 @@ mod tests {
|
||||||
assert_eq!(recvd, sent);
|
assert_eq!(recvd, sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test sending 2 different frames.
|
#[test]
|
||||||
|
fn two_frames_sequentially() {
|
||||||
|
let (mut tx, mut rx) = pair();
|
||||||
|
{
|
||||||
|
let sent = [0x00, 0x01, 0x02];
|
||||||
|
tx.send(&sent).unwrap();
|
||||||
|
let recvd = rx.recv().unwrap();
|
||||||
|
assert_eq!(recvd, sent);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Test buffering:
|
{
|
||||||
// * Write half a frame
|
let sent = [0x10, 0x11, 0x12];
|
||||||
// * recv() returns nothing
|
tx.send(&sent).unwrap();
|
||||||
// * Write the rest of the frame
|
let recvd = rx.recv().unwrap();
|
||||||
// * recv() returns the whole frame
|
assert_eq!(recvd, sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_frames_at_once() {
|
||||||
|
let (mut tx, mut rx) = pair();
|
||||||
|
let s1 = [0x00, 0x01, 0x02];
|
||||||
|
let s2 = [0x10, 0x11, 0x12];
|
||||||
|
|
||||||
|
tx.send(&s1).unwrap();
|
||||||
|
tx.send(&s2).unwrap();
|
||||||
|
|
||||||
|
let r1 = rx.recv().unwrap();
|
||||||
|
let r2 = rx.recv().unwrap();
|
||||||
|
println!("r1: {:?}\n\
|
||||||
|
r2: {:?}", r1, r2);
|
||||||
|
|
||||||
|
assert_eq!(r1, s1);
|
||||||
|
assert_eq!(r2, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_input() {
|
||||||
|
let (mut _tx, mut rx) = pair();
|
||||||
|
match rx.recv() {
|
||||||
|
Err(Error(ErrorKind::EofDuringFrame, _)) => (),
|
||||||
|
e @ _ => panic!("Bad value: {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn pair() -> (Sender<impl Write>, Receiver<impl Read>) {
|
fn pair() -> (Sender<impl Write>, Receiver<impl Read>) {
|
||||||
let c = Channel::new();
|
let c = Channel::new();
|
||||||
|
|
Loading…
Reference in a new issue