use std::{error::Error as StdError, fmt, io, str::FromStr};
pub mod console;
#[cfg(test)]
mod tests;
type BoxResult<T> = Result<T, Box<StdError + Send + Sync + 'static>>;
pub trait INode {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn as_node_mut(&mut self) -> NodeMut<'_>;
fn as_inode_mut(&mut self) -> &mut INode;
}
#[derive(Debug)]
pub enum NodeMut<'a> {
Prop(&'a mut IProperty),
List(&'a mut IList),
Action(&'a mut IAction),
}
impl INode for NodeMut<'_> {
fn name(&self) -> &str {
match self {
NodeMut::Prop(prop) => prop.name(),
NodeMut::List(list) => list.name(),
NodeMut::Action(act) => act.name(),
}
}
fn description(&self) -> &str {
match self {
NodeMut::Prop(prop) => prop.description(),
NodeMut::List(list) => list.description(),
NodeMut::Action(act) => act.description(),
}
}
fn as_node_mut(&mut self) -> NodeMut<'_> {
match self {
NodeMut::Prop(prop) => NodeMut::Prop(*prop),
NodeMut::List(list) => NodeMut::List(*list),
NodeMut::Action(act) => NodeMut::Action(*act),
}
}
fn as_inode_mut(&mut self) -> &mut INode {
self
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum PropState {
Default,
UserSet,
Invalid,
}
pub trait IProperty: INode {
fn get(&self) -> String;
fn set(&mut self, val: &str) -> BoxResult<()>;
fn reset(&mut self);
fn default(&self) -> String;
fn state(&self) -> PropState;
fn flags(&self) -> u32 {
0
}
fn values(&self) -> Option<&[&str]> {
None
}
}
impl fmt::Debug for IProperty + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IProperty")
.field("name", &self.name())
.field("desc", &self.description())
.field("value", &self.get())
.field("default", &self.default())
.field("state", &self.state())
.field("flags", &self.flags())
.field("values", &self.values())
.finish()
}
}
pub struct Property<'a, T> {
name: &'a str,
desc: &'a str,
variable: &'a mut T,
default: T,
}
#[allow(non_snake_case)]
pub fn Property<'a, T>(name: &'a str, desc: &'a str, variable: &'a mut T, default: T) -> Property<'a, T> {
Property { name, desc, variable, default }
}
impl<'a, T> Property<'a, T> {
pub fn new(name: &'a str, desc: &'a str, variable: &'a mut T, default: T) -> Property<'a, T> {
Property { name, desc, variable, default }
}
}
impl<'a, T> INode for Property<'a, T>
where T: FromStr + ToString + Clone + PartialEq,
T::Err: StdError + Send + Sync + 'static
{
fn name(&self) -> &str {
self.name
}
fn description(&self) -> &str {
self.desc
}
fn as_node_mut(&mut self) -> NodeMut<'_> {
NodeMut::Prop(self)
}
fn as_inode_mut(&mut self) -> &mut INode {
self
}
}
impl<'a, T> IProperty for Property<'a, T>
where T: FromStr + ToString + Clone + PartialEq,
T::Err: StdError + Send + Sync + 'static
{
fn get(&self) -> String {
self.variable.to_string()
}
fn set(&mut self, val: &str) -> BoxResult<()> {
*self.variable = T::from_str(val)?;
Ok(())
}
fn reset(&mut self) {
self.variable.clone_from(&self.default);
}
fn default(&self) -> String {
self.default.to_string()
}
fn state(&self) -> PropState {
match *self.variable == self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub struct ClampedProp<'a, T> {
name: &'a str,
desc: &'a str,
variable: &'a mut T,
default: T,
min: T,
max: T,
}
#[allow(non_snake_case)]
pub fn ClampedProp<'a, T>(name: &'a str, desc: &'a str, variable: &'a mut T, default: T, min: T, max: T) -> ClampedProp<'a, T> {
ClampedProp { name, desc, variable, default, min, max }
}
impl<'a, T> ClampedProp<'a, T> {
pub fn new(name: &'a str, desc: &'a str, variable: &'a mut T, default: T, min: T, max: T) -> ClampedProp<'a, T> {
ClampedProp { name, desc, variable, default, min, max }
}
}
impl<'a, T> INode for ClampedProp<'a, T>
where T: FromStr + ToString + Clone + PartialEq + PartialOrd,
T::Err: StdError + Send + Sync + 'static
{
fn name(&self) -> &str {
self.name
}
fn description(&self) -> &str {
self.desc
}
fn as_node_mut(&mut self) -> NodeMut<'_> {
NodeMut::Prop(self)
}
fn as_inode_mut(&mut self) -> &mut INode {
self
}
}
impl<'a, T> IProperty for ClampedProp<'a, T>
where T: FromStr + ToString + Clone + PartialEq + PartialOrd,
T::Err: StdError + Send + Sync + 'static
{
fn get(&self) -> String {
self.variable.to_string()
}
fn set(&mut self, val: &str) -> BoxResult<()> {
*self.variable = T::from_str(val)?;
if *self.variable < self.min {
self.variable.clone_from(&self.min);
}
if *self.variable > self.max {
self.variable.clone_from(&self.max);
}
Ok(())
}
fn reset(&mut self) {
self.variable.clone_from(&self.default);
}
fn default(&self) -> String {
self.default.to_string()
}
fn state(&self) -> PropState {
match *self.variable == self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub struct ReadOnlyProp<'a, T> {
name: &'a str,
desc: &'a str,
variable: &'a T,
default: T,
}
#[allow(non_snake_case)]
pub fn ReadOnlyProp<'a, T>(name: &'a str, desc: &'a str, variable: &'a T, default: T) -> ReadOnlyProp<'a, T> {
ReadOnlyProp { name, desc, variable, default }
}
impl<'a, T> ReadOnlyProp<'a, T> {
pub fn new(name: &'a str, desc: &'a str, variable: &'a T, default: T) -> ReadOnlyProp<'a, T> {
ReadOnlyProp { name, desc, variable, default }
}
}
impl<'a, T: ToString + PartialEq> INode for ReadOnlyProp<'a, T> {
fn name(&self) -> &str {
self.name
}
fn description(&self) -> &str {
self.desc
}
fn as_node_mut(&mut self) -> NodeMut<'_> {
NodeMut::Prop(self)
}
fn as_inode_mut(&mut self) -> &mut INode {
self
}
}
impl<'a, T: ToString + PartialEq> IProperty for ReadOnlyProp<'a, T> {
fn get(&self) -> String {
self.variable.to_string()
}
fn set(&mut self, _val: &str) -> BoxResult<()> {
Err("cannot set read-only property".into())
}
fn reset(&mut self) {}
fn default(&self) -> String {
self.default.to_string()
}
fn state(&self) -> PropState {
match *self.variable == self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub struct OwnedProp<T> {
pub name: String,
pub variable: T,
pub default: T,
_private: (),
}
#[allow(non_snake_case)]
pub fn OwnedProp<T>(name: String, variable: T, default: T) -> OwnedProp<T> {
OwnedProp { name, variable, default, _private: () }
}
impl<T> OwnedProp<T> {
pub fn new(name: String, variable: T, default: T) -> OwnedProp<T> {
OwnedProp { name, variable, default, _private: () }
}
}
impl<T> INode for OwnedProp<T>
where T: FromStr + ToString + Clone + PartialEq,
T::Err: StdError + Send + Sync + 'static
{
fn name(&self) -> &str { &self.name }
fn description(&self) -> &str { "" }
fn as_node_mut(&mut self) -> NodeMut<'_> { NodeMut::Prop(self) }
fn as_inode_mut(&mut self) -> &mut INode { self }
}
impl<T> IProperty for OwnedProp<T>
where T: FromStr + ToString + Clone + PartialEq,
T::Err: StdError + Send + Sync + 'static
{
fn get(&self) -> String {
self.variable.to_string()
}
fn set(&mut self, val: &str) -> BoxResult<()> {
self.variable = T::from_str(val)?;
Ok(())
}
fn reset(&mut self) {
self.variable.clone_from(&self.default);
}
fn default(&self) -> String {
self.default.to_string()
}
fn state(&self) -> PropState {
match self.variable == self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub trait IVisit {
fn visit_mut(&mut self, f: &mut FnMut(&mut INode));
}
impl fmt::Debug for IVisit + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("IVisit { .. }")
}
}
#[derive(Copy, Clone, Debug)]
pub struct VisitMut<F: FnMut(&mut FnMut(&mut INode))>(pub F);
impl<F: FnMut(&mut FnMut(&mut INode))> IVisit for VisitMut<F> {
fn visit_mut(&mut self, f: &mut FnMut(&mut INode)) {
(self.0)(f)
}
}
pub trait IList: INode {
fn as_visit_mut(&mut self) -> &mut IVisit;
}
impl fmt::Debug for IList + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IList")
.field("name", &self.name())
.field("desc", &self.description())
.finish()
}
}
#[derive(Debug)]
pub struct List<'a, V> {
name: &'a str,
desc: &'a str,
visitor: &'a mut V,
}
#[allow(non_snake_case)]
pub fn List<'a, V>(name: &'a str, desc: &'a str, visitor: &'a mut V) -> List<'a, V> {
List { name, desc, visitor }
}
impl<'a, V> List<'a, V> {
pub fn new(name: &'a str, desc: &'a str, visitor: &'a mut V) -> List<'a, V> {
List { name, desc, visitor }
}
}
impl<'a, V: IVisit> INode for List<'a, V> {
fn name(&self) -> &str {
self.name
}
fn description(&self) -> &str {
self.desc
}
fn as_node_mut(&mut self) -> NodeMut<'_> {
NodeMut::List(self)
}
fn as_inode_mut(&mut self) -> &mut INode {
self
}
}
impl<'a, V: IVisit> IList for List<'a, V> {
fn as_visit_mut(&mut self) -> &mut IVisit {
self.visitor
}
}
pub trait IConsole: fmt::Write {
fn write_error(&mut self, err: &(StdError + 'static));
}
impl IConsole for String {
fn write_error(&mut self, err: &(StdError + 'static)) {
let _ = writeln!(self as &mut fmt::Write, "error: {}", err);
}
}
pub struct NullConsole;
impl fmt::Write for NullConsole {
fn write_str(&mut self, _s: &str) -> fmt::Result { Ok(()) }
fn write_char(&mut self, _c: char) -> fmt::Result { Ok(()) }
fn write_fmt(&mut self, _args: fmt::Arguments) -> fmt::Result { Ok(()) }
}
impl IConsole for NullConsole {
fn write_error(&mut self, _err: &(StdError + 'static)) {}
}
pub struct IoConsole<W>(pub W);
impl<W: io::Write> fmt::Write for IoConsole<W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
io::Write::write_all(&mut self.0, s.as_bytes()).map_err(|_| fmt::Error)
}
fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
io::Write::write_fmt(&mut self.0, args).map_err(|_| fmt::Error)
}
}
impl<W: io::Write> IConsole for IoConsole<W> {
fn write_error(&mut self, err: &(StdError + 'static)) {
let _ = writeln!(self.0, "error: {}", err);
}
}
impl IoConsole<io::Stdout> {
pub fn stdout() -> IoConsole<io::Stdout> {
IoConsole(io::stdout())
}
}
impl IoConsole<io::Stderr> {
pub fn stderr() -> IoConsole<io::Stderr> {
IoConsole(io::stderr())
}
}
pub trait IAction: INode {
fn invoke(&mut self, args: &[&str], console: &mut IConsole);
}
impl fmt::Debug for IAction + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IAction")
.field("name", &self.name())
.field("desc", &self.description())
.finish()
}
}
#[derive(Debug)]
pub struct Action<'a, F: FnMut(&[&str], &mut IConsole)> {
name: &'a str,
desc: &'a str,
invoke: F,
}
#[allow(non_snake_case)]
pub fn Action<'a, F: FnMut(&[&str], &mut IConsole)>(name: &'a str, desc: &'a str, invoke: F) -> Action<'a, F> {
Action { name, desc, invoke }
}
impl<'a, F: FnMut(&[&str], &mut IConsole)> Action<'a, F> {
pub fn new(name: &'a str, desc: &'a str, invoke: F) -> Action<'a, F> {
Action { name, desc, invoke }
}
}
impl<'a, F: FnMut(&[&str], &mut IConsole)> INode for Action<'a, F> {
fn name(&self) -> &str {
self.name
}
fn description(&self) -> &str {
self.desc
}
fn as_node_mut(&mut self) -> NodeMut<'_> {
NodeMut::Action(self)
}
fn as_inode_mut(&mut self) -> &mut INode {
self
}
}
impl<'a, F: FnMut(&[&str], &mut IConsole)> IAction for Action<'a, F> {
fn invoke(&mut self, args: &[&str], console: &mut IConsole) {
(self.invoke)(args, console)
}
}