use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::Display;
use std::io::{self, Write};
mod conf;
mod delim;
mod offset;
pub use self::conf::Configuration;
pub use self::delim::Delimiters;
pub use self::offset::Offset;
pub trait Contextual: Any {}
impl Contextual for Configuration {}
impl Contextual for Delimiters {}
impl Contextual for Offset {}
#[derive(Debug)]
pub struct Context {
data: HashMap<TypeId, Box<Any>>,
}
impl Context {
pub fn new() -> Self {
let mut ctx = Self {
data: HashMap::new(),
};
let conf = Configuration::new();
let delim = Delimiters::new(&conf);
ctx.insert(conf);
ctx.insert(delim);
ctx
}
pub fn get<T: Contextual>(&self) -> Option<&T>
where
T: Contextual,
{
let types = TypeId::of::<T>();
self.data.get(&types).and_then(|b| b.downcast_ref::<T>())
}
pub fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: Contextual,
{
let types = TypeId::of::<T>();
self.data
.get_mut(&types)
.and_then(|b| b.downcast_mut::<T>())
}
pub fn insert<T>(&mut self, t: T)
where
T: Contextual,
{
let types = TypeId::of::<T>();
self.data.insert(types, Box::new(t));
}
pub fn take<T>(&mut self) -> Option<T>
where
T: Contextual,
{
let types = TypeId::of::<T>();
self.data
.remove(&types)
.and_then(|b| b.downcast::<T>().ok())
.map(|t| *t)
}
#[inline]
pub fn write(&mut self, key: &[u8], val: &[u8]) {
let out = self.get::<Delimiters>().unwrap().output();
let stdout = io::stdout();
let mut lock = stdout.lock();
lock.write_all(key).unwrap();
lock.write_all(out).unwrap();
lock.write_all(val).unwrap();
lock.write_all(b"\n").unwrap();
}
#[inline]
pub fn write_fmt<K, V>(&mut self, key: K, val: V)
where
K: Display,
V: Display,
{
self.write(key.to_string().as_bytes(), val.to_string().as_bytes());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_context_creation() {
let ctx = Context::new();
assert!(ctx.get::<Configuration>().is_some());
assert!(ctx.get::<Delimiters>().is_some());
}
#[test]
fn test_context_insertion() {
let mut ctx = Context::new();
let val = TestStruct(0);
ctx.insert(val);
assert!(ctx.get::<TestStruct>().is_some());
}
#[test]
fn test_mutable_references() {
let mut ctx = Context::new();
let val = TestStruct(0);
ctx.insert(val);
{
let mref = ctx.get_mut::<TestStruct>();
assert!(mref.is_some());
mref.unwrap().0 = 1;
}
let iref = ctx.get::<TestStruct>();
assert!(iref.is_some());
assert_eq!(iref.unwrap().0, 1);
}
#[test]
fn test_taking_values() {
let mut ctx = Context::new();
let val = TestStruct(0);
ctx.insert(val);
let take = ctx.take::<TestStruct>();
assert!(take.is_some());
let take = ctx.take::<TestStruct>();
assert!(take.is_none());
}
struct TestStruct(usize);
impl Contextual for TestStruct {}
}