use std::cell::RefCell; use std::collections::BTreeMap; use std::rc::Rc; use syscall::Result as SyscallResult; pub type CfgNodeRef = Rc>; pub trait NodeWriter { fn write_line(&mut self, _: &str) -> SyscallResult<()> { Ok(()) } fn commit(&mut self) -> SyscallResult<()> { Ok(()) } } pub struct SimpleWriter where WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, C: 'static + Fn(&mut T) -> SyscallResult<()>, { data: T, write_line: WL, commit: C, } impl NodeWriter for SimpleWriter where WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, C: 'static + Fn(&mut T) -> SyscallResult<()>, { fn write_line(&mut self, line: &str) -> SyscallResult<()> { (self.write_line)(&mut self.data, line) } fn commit(&mut self) -> SyscallResult<()> { (self.commit)(&mut self.data) } } impl SimpleWriter where WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>, C: 'static + Fn(&mut T) -> SyscallResult<()>, { pub fn new_boxed(data: T, write_line: WL, commit: C) -> Box { Box::new(SimpleWriter { data, write_line, commit, }) } } pub trait CfgNode { fn is_dir(&self) -> bool { false } fn is_writable(&self) -> bool { false } fn is_readable(&self) -> bool { true } fn read(&self) -> String { String::new() } fn open(&self, _file: &str) -> Option { None } fn new_writer(&self) -> Option> { None } } pub struct RONode where F: Fn() -> String, { read_fun: F, } impl CfgNode for RONode where F: Fn() -> String, { fn read(&self) -> String { (self.read_fun)() } } impl RONode where F: 'static + Fn() -> String, { pub fn new_ref(read_fun: F) -> CfgNodeRef { Rc::new(RefCell::new(RONode { read_fun })) } } pub struct WONode where W: 'static + Fn() -> Box, { new_writer: W, } impl CfgNode for WONode where W: 'static + Fn() -> Box, { fn is_readable(&self) -> bool { false } fn is_writable(&self) -> bool { true } fn new_writer(&self) -> Option> { Some((self.new_writer)()) } } impl WONode where W: 'static + Fn() -> Box, { pub fn new_ref(new_writer: W) -> CfgNodeRef { Rc::new(RefCell::new(WONode { new_writer })) } } pub struct RWNode where F: Fn() -> String, W: 'static + Fn() -> Box, { read_fun: F, new_writer: W, } impl CfgNode for RWNode where F: Fn() -> String, W: 'static + Fn() -> Box, { fn read(&self) -> String { (self.read_fun)() } fn is_writable(&self) -> bool { true } fn new_writer(&self) -> Option> { Some((self.new_writer)()) } } impl RWNode where F: 'static + Fn() -> String, W: 'static + Fn() -> Box, { pub fn new_ref(read_fun: F, new_writer: W) -> CfgNodeRef { Rc::new(RefCell::new(RWNode { read_fun, new_writer, })) } } pub struct StaticDirNode { child_nodes: BTreeMap, } impl CfgNode for StaticDirNode { fn is_dir(&self) -> bool { true } fn read(&self) -> String { let mut files = String::new(); for child in self.child_nodes.keys() { if !files.is_empty() { files.push('\n'); } files += child; } files } fn open(&self, file: &str) -> Option { self.child_nodes.get(file).map(|node| Rc::clone(node)) } } impl StaticDirNode { pub fn new_ref(child_nodes: BTreeMap) -> CfgNodeRef { Rc::new(RefCell::new(StaticDirNode { child_nodes })) } } macro_rules! cfg_node { (val $e:expr) => { $e }; (ro [ $($c:ident),* ] || $b:block ) => { { $(let $c = $c.clone();)* RONode::new_ref(move|| $b) } }; (wo [ $($c:ident),* ] ( $et:ty , $e:expr ) |$data_i:ident, $line_i:ident| $write_line:block |$data_i2:ident| $commit:block) => { { $(#[allow(unused_variables)] let $c = $c.clone();)* let new_writer = move || -> Box { let write_line = { $(#[allow(unused_variables)] let $c = $c.clone();)* move |$data_i: &mut $et, $line_i: &str| $write_line }; let commit = { $(#[allow(unused_variables)] let $c = $c.clone();)* move |$data_i2: &mut $et| $commit }; let data: $et = $e; SimpleWriter::new_boxed(data, write_line, commit) }; WONode::new_ref(new_writer) } }; (rw [ $($c:ident),* ] ( $et:ty , $e:expr ) || $read_fun:block |$data_i:ident, $line_i:ident| $write_line:block |$data_i2:ident| $commit:block) => { { let read_fun = { $(#[allow(unused_variables)] let $c = $c.clone();)* move || $read_fun }; $(#[allow(unused_variables)] let $c = $c.clone();)* let new_writer = move || -> Box { let write_line = { $(#[allow(unused_variables)] let $c = $c.clone();)* move |$data_i: &mut $et, $line_i: &str| $write_line }; let commit = { $(#[allow(unused_variables)] let $c = $c.clone();)* move |$data_i2: &mut $et| $commit }; let data: $et = $e; SimpleWriter::new_boxed(data, write_line, commit) }; RWNode::new_ref(read_fun, new_writer) } }; ($($e:expr => { $($t:tt)* }),* $(,)*) => { { let mut children = BTreeMap::new(); $(children.insert($e.into(), cfg_node!($($t)*));)* StaticDirNode::new_ref(children) } }; }