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]
|
||||
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"),
|
||||
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
|
||||
//! [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)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
@ -17,12 +28,16 @@
|
|||
extern crate cobs;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate ref_slice;
|
||||
|
||||
pub mod channel;
|
||||
|
||||
pub mod error;
|
||||
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.
|
||||
pub struct Sender<W: Write> {
|
||||
|
@ -37,7 +52,11 @@ impl<W: Write> Sender<W> {
|
|||
}
|
||||
|
||||
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)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -45,6 +64,7 @@ impl<W: Write> Sender<W> {
|
|||
|
||||
/// Receives frames from an underlying `io::Read` instance.
|
||||
pub struct Receiver<R: Read> {
|
||||
/// The underlying reader
|
||||
r: R,
|
||||
}
|
||||
|
||||
|
@ -56,9 +76,35 @@ impl<R: Read> Receiver<R> {
|
|||
}
|
||||
|
||||
pub fn recv(&mut self) -> Result<Vec<u8>> {
|
||||
let mut code = Vec::new();
|
||||
self.r.read_to_end(&mut code)?;
|
||||
cobs::decode_vec(&code)
|
||||
let mut next_frame = Vec::new();
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +112,7 @@ impl<R: Read> Receiver<R> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use channel::Channel;
|
||||
use error::{Error, ErrorKind};
|
||||
use std::io::{Read, Write};
|
||||
use super::{Receiver, Sender};
|
||||
|
||||
|
@ -78,13 +125,50 @@ mod tests {
|
|||
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
|
||||
// * recv() returns nothing
|
||||
// * Write the rest of the frame
|
||||
// * recv() returns the whole frame
|
||||
{
|
||||
let sent = [0x10, 0x11, 0x12];
|
||||
tx.send(&sent).unwrap();
|
||||
let recvd = rx.recv().unwrap();
|
||||
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>) {
|
||||
let c = Channel::new();
|
||||
|
|
Loading…
Reference in a new issue