use std::{any, error::Error as StdError, fmt, io, str::FromStr};
pub mod console;
#[cfg(test)]
mod tests;
pub trait INode {
fn name(&self) -> &str;
fn as_node(&mut self) -> Node<'_>;
fn as_inode(&mut self) -> &mut dyn INode;
}
#[derive(Debug)]
pub enum Node<'a> {
Prop(&'a mut dyn IProperty),
List(&'a mut dyn IList),
Action(&'a mut dyn IAction),
}
impl INode for Node<'_> {
fn name(&self) -> &str {
match self {
Node::Prop(prop) => prop.name(),
Node::List(list) => list.name(),
Node::Action(act) => act.name(),
}
}
fn as_node(&mut self) -> Node<'_> {
match self {
Node::Prop(prop) => Node::Prop(*prop),
Node::List(list) => Node::List(*list),
Node::Action(act) => Node::Action(*act),
}
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
pub trait IValue: any::Any + fmt::Display {
fn as_any(&self) -> &dyn any::Any;
#[cfg(feature = "type_name")]
fn type_name(&self) -> &str {
any::type_name::<Self>()
}
}
impl fmt::Debug for dyn IValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl dyn IValue {
#[inline]
pub fn is<T: any::Any>(&self) -> bool {
any::TypeId::of::<T>() == self.type_id()
}
#[inline]
pub fn downcast_ref<T: any::Any>(&self) -> Option<&T> {
if self.is::<T>() {
Some(unsafe { &*(self as *const dyn IValue as *const T) })
}
else {
None
}
}
}
impl<T: 'static + Sized> IValue for T
where T: Clone + Default + PartialEq + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn as_any(&self) -> &dyn any::Any {
self
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum PropState {
Default,
UserSet,
Invalid,
}
pub trait IProperty: INode {
fn get_value(&self) -> &dyn IValue;
fn set_value(&mut self, val: &dyn IValue, writer: &mut dyn IWrite) -> bool;
fn set(&mut self, val: &str, writer: &mut dyn IWrite) -> bool;
fn reset(&mut self);
fn default_value(&self) -> &dyn IValue;
fn state(&self) -> PropState;
fn flags(&self) -> u32 {
0
}
#[cfg(feature = "type_name")]
fn type_name(&self) -> &str {
any::type_name::<Self>()
}
fn values(&self) -> Option<&[&str]> {
None
}
}
impl fmt::Debug for dyn IProperty + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug = f.debug_struct("IProperty");
debug.field("name", &self.name());
debug.field("value", &self.get_value());
debug.field("default", &self.default_value());
debug.field("state", &self.state());
debug.field("flags", &self.flags());
#[cfg(feature = "type_name")]
debug.field("type", &self.type_name());
debug.field("values", &self.values());
debug.finish_non_exhaustive()
}
}
pub struct Property<'a, 'x, T: 'static> {
name: &'a str,
variable: &'x mut T,
default: &'a T,
}
#[allow(non_snake_case)]
#[inline]
pub fn Property<'a, 'x, T>(name: &'a str, variable: &'x mut T, default: &'a T) -> Property<'a, 'x, T> {
Property { name, variable, default }
}
impl<'a, 'x, T> Property<'a, 'x, T> {
#[inline]
pub fn new(name: &'a str, variable: &'x mut T, default: &'a T) -> Property<'a, 'x, T> {
Property { name, variable, default }
}
}
impl<'a, 'x, T> INode for Property<'a, 'x, T>
where T: Clone + Default + PartialEq + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn name(&self) -> &str {
self.name
}
fn as_node(&mut self) -> Node<'_> {
Node::Prop(self)
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
impl<'a, 'x, T> IProperty for Property<'a, 'x, T>
where T: Clone + Default + PartialEq + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn get_value(&self) -> &dyn IValue {
&*self.variable
}
fn set_value(&mut self, val: &dyn IValue, writer: &mut dyn IWrite) -> bool {
if let Some(val) = val.downcast_ref::<T>() {
self.variable.clone_from(val);
true
}
else {
let _ = write_mismatched_types::<T>(writer, val);
false
}
}
fn set(&mut self, val: &str, writer: &mut dyn IWrite) -> bool {
match val.parse::<T>() {
Ok(val) => {
*self.variable = val;
true
},
Err(err) => {
let _ = write_error(writer, &err);
false
},
}
}
fn reset(&mut self) {
self.variable.clone_from(&self.default);
}
fn default_value(&self) -> &dyn IValue {
&*self.default
}
fn state(&self) -> PropState {
match *self.variable == *self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
#[inline]
fn check_bounds_inclusive<T: PartialOrd>(val: &T, min: Option<&T>, max: Option<&T>) -> bool {
if let Some(min) = min {
if *val >= *min {
return true;
}
return false;
}
if let Some(max) = max {
if *val <= *max {
return true;
}
return false;
}
return true;
}
pub struct ClampedProp<'a, 'x, T: 'static> {
name: &'a str,
variable: &'x mut T,
default: &'a T,
min: Option<&'a T>,
max: Option<&'a T>,
}
#[allow(non_snake_case)]
#[inline]
pub fn ClampedProp<'a, 'x, T>(name: &'a str, variable: &'x mut T, default: &'a T, min: Option<&'a T>, max: Option<&'a T>) -> ClampedProp<'a, 'x, T> {
ClampedProp { name, variable, default, min, max }
}
impl<'a, 'x, T> ClampedProp<'a, 'x, T> {
#[inline]
pub fn new(name: &'a str, variable: &'x mut T, default: &'a T, min: Option<&'a T>, max: Option<&'a T>) -> ClampedProp<'a, 'x, T> {
ClampedProp { name, variable, default, min, max }
}
}
impl<'a, 'x, T> INode for ClampedProp<'a, 'x, T>
where T: Clone + Default + PartialEq + PartialOrd + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn name(&self) -> &str {
self.name
}
fn as_node(&mut self) -> Node<'_> {
Node::Prop(self)
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
impl<'a, 'x, T> IProperty for ClampedProp<'a, 'x, T>
where T: Clone + Default + PartialEq + PartialOrd + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn get_value(&self) -> &dyn IValue {
&*self.variable
}
fn set_value(&mut self, val: &dyn IValue, writer: &mut dyn IWrite) -> bool {
if let Some(val) = val.downcast_ref::<T>() {
if check_bounds_inclusive(val, self.min, self.max) {
self.variable.clone_from(val);
}
true
}
else {
let _ = write_mismatched_types::<T>(writer, val);
false
}
}
fn set(&mut self, val: &str, writer: &mut dyn IWrite) -> bool {
match val.parse::<T>() {
Ok(val) => {
if check_bounds_inclusive(&val, self.min, self.max) {
*self.variable = val;
}
true
},
Err(err) => {
let _ = write_error(writer, &err);
false
},
}
}
fn reset(&mut self) {
self.variable.clone_from(&self.default);
}
fn default_value(&self) -> &dyn IValue {
&*self.default
}
fn state(&self) -> PropState {
match *self.variable == *self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub struct ReadOnlyProp<'a, T: 'static> {
name: &'a str,
variable: &'a T,
default: &'a T,
}
#[allow(non_snake_case)]
#[inline]
pub fn ReadOnlyProp<'a, T>(name: &'a str, variable: &'a T, default: &'a T) -> ReadOnlyProp<'a, T> {
ReadOnlyProp { name, variable, default }
}
impl<'a, T> ReadOnlyProp<'a, T> {
#[inline]
pub fn new(name: &'a str, variable: &'a T, default: &'a T) -> ReadOnlyProp<'a, T> {
ReadOnlyProp { name, variable, default }
}
}
impl<'a, T: PartialEq + IValue> INode for ReadOnlyProp<'a, T> {
fn name(&self) -> &str {
self.name
}
fn as_node(&mut self) -> Node<'_> {
Node::Prop(self)
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
impl<'a, T: PartialEq + IValue> IProperty for ReadOnlyProp<'a, T> {
fn get_value(&self) -> &dyn IValue {
&*self.variable
}
fn set_value(&mut self, _val: &dyn IValue, writer: &mut dyn IWrite) -> bool {
let _ = writer.write_str("cannot set read-only property");
false
}
fn set(&mut self, _val: &str, writer: &mut dyn IWrite) -> bool {
let _ = writer.write_str("cannot set read-only property");
false
}
fn reset(&mut self) {}
fn default_value(&self) -> &dyn IValue {
&*self.default
}
fn state(&self) -> PropState {
match *self.variable == *self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub struct OwnedProp<T: 'static> {
pub name: String,
pub variable: T,
pub default: T,
_private: (),
}
#[allow(non_snake_case)]
#[inline]
pub fn OwnedProp<T>(name: String, variable: T, default: T) -> OwnedProp<T> {
OwnedProp { name, variable, default, _private: () }
}
impl<T> OwnedProp<T> {
#[inline]
pub fn new(name: String, variable: T, default: T) -> OwnedProp<T> {
OwnedProp { name, variable, default, _private: () }
}
}
impl<T> INode for OwnedProp<T>
where T: Clone + Default + PartialEq + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn name(&self) -> &str {
&self.name
}
fn as_node(&mut self) -> Node<'_> {
Node::Prop(self)
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
impl<T> IProperty for OwnedProp<T>
where T: Clone + Default + PartialEq + fmt::Display + FromStr,
T::Err: StdError + Send + Sync + 'static
{
fn get_value(&self) -> &dyn IValue {
&self.variable
}
fn set_value(&mut self, val: &dyn IValue, writer: &mut dyn IWrite) -> bool {
if let Some(val) = val.downcast_ref::<T>() {
self.variable.clone_from(val);
true
}
else {
let _ = write_mismatched_types::<T>(writer, val);
false
}
}
fn set(&mut self, val: &str, writer: &mut dyn IWrite) -> bool {
match val.parse::<T>() {
Ok(val) => {
self.variable = val;
true
},
Err(err) => {
let _ = write_error(writer, &err);
false
},
}
}
fn reset(&mut self) {
self.variable.clone_from(&self.default);
}
fn default_value(&self) -> &dyn IValue {
&self.default
}
fn state(&self) -> PropState {
match self.variable == self.default {
true => PropState::Default,
false => PropState::UserSet,
}
}
}
pub trait IVisit {
fn visit(&mut self, f: &mut dyn FnMut(&mut dyn INode));
}
impl fmt::Debug for dyn IVisit + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IVisit").finish_non_exhaustive()
}
}
#[derive(Copy, Clone, Debug)]
pub struct Visit<F: FnMut(&mut dyn FnMut(&mut dyn INode))>(pub F);
impl<F: FnMut(&mut dyn FnMut(&mut dyn INode))> IVisit for Visit<F> {
fn visit(&mut self, f: &mut dyn FnMut(&mut dyn INode)) {
let Self(this) = self;
this(f)
}
}
pub trait IList: INode {
fn as_ivisit(&mut self) -> &mut dyn IVisit;
}
impl fmt::Debug for dyn IList + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IList")
.field("name", &self.name())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct List<'a, 'x> {
name: &'a str,
visitor: &'x mut dyn IVisit,
}
#[allow(non_snake_case)]
#[inline]
pub fn List<'a, 'x>(name: &'a str, visitor: &'x mut dyn IVisit) -> List<'a, 'x> {
List { name, visitor }
}
impl<'a, 'x> List<'a, 'x> {
#[inline]
pub fn new(name: &'a str, visitor: &'x mut dyn IVisit) -> List<'a, 'x> {
List { name, visitor }
}
}
impl<'a, 'x> INode for List<'a, 'x> {
fn name(&self) -> &str {
self.name
}
fn as_node(&mut self) -> Node<'_> {
Node::List(self)
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
impl<'a, 'x> IList for List<'a, 'x> {
fn as_ivisit(&mut self) -> &mut dyn IVisit {
self.visitor
}
}
pub trait IWrite: any::Any + fmt::Write {}
impl dyn IWrite {
#[inline]
pub fn is<T: any::Any>(&self) -> bool {
any::TypeId::of::<T>() == self.type_id()
}
#[inline]
pub fn downcast_ref<T: any::Any>(&self) -> Option<&T> {
if self.is::<T>() {
Some(unsafe { &*(self as *const dyn IWrite as *const T) })
}
else {
None
}
}
#[inline]
pub fn downcast_mut<T: any::Any>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
Some(unsafe { &mut *(self as *mut dyn IWrite as *mut T) })
}
else {
None
}
}
}
#[inline]
fn write_error<T: ?Sized + StdError>(writer: &mut dyn IWrite, v: &T) -> fmt::Result {
writer.write_fmt(format_args!("{}", v))
}
#[cfg(feature = "type_name")]
#[inline]
fn write_mismatched_types<T: IValue>(writer: &mut dyn IWrite, val: &dyn IValue) -> fmt::Result {
write!(writer, "mismatched types: expected `{}`, found `{}`", any::type_name::<T>(), val.type_name())
}
#[cfg(not(feature = "type_name"))]
#[inline]
fn write_mismatched_types<T: IValue>(writer: &mut dyn IWrite, _val: &dyn IValue) -> fmt::Result {
writer.write_str("mismatched types")
}
impl IWrite for String {}
pub struct NullWriter;
impl fmt::Write for NullWriter {
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 IWrite for NullWriter {}
pub struct IoWriter<W>(pub W);
impl<W: io::Write> fmt::Write for IoWriter<W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let Self(this) = self;
io::Write::write_all(this, s.as_bytes()).map_err(|_| fmt::Error)
}
fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
let Self(this) = self;
io::Write::write_fmt(this, args).map_err(|_| fmt::Error)
}
}
impl<W: io::Write + 'static> IWrite for IoWriter<W> {}
impl IoWriter<io::Stdout> {
#[inline]
pub fn stdout() -> IoWriter<io::Stdout> {
IoWriter(io::stdout())
}
}
impl IoWriter<io::Stderr> {
#[inline]
pub fn stderr() -> IoWriter<io::Stderr> {
IoWriter(io::stderr())
}
}
pub trait IAction: INode {
fn invoke(&mut self, args: &str, writer: &mut dyn IWrite);
}
impl fmt::Debug for dyn IAction + '_ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IAction")
.field("name", &self.name())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct Action<'a, F: FnMut(&str, &mut dyn IWrite)> {
name: &'a str,
invoke: F,
}
#[allow(non_snake_case)]
#[inline]
pub fn Action<'a, F: FnMut(&str, &mut dyn IWrite)>(name: &'a str, invoke: F) -> Action<'a, F> {
Action { name, invoke }
}
impl<'a, F: FnMut(&str, &mut dyn IWrite)> Action<'a, F> {
#[inline]
pub fn new(name: &'a str, invoke: F) -> Action<'a, F> {
Action { name, invoke }
}
}
impl<'a, F: FnMut(&str, &mut dyn IWrite)> INode for Action<'a, F> {
fn name(&self) -> &str {
self.name
}
fn as_node(&mut self) -> Node<'_> {
Node::Action(self)
}
fn as_inode(&mut self) -> &mut dyn INode {
self
}
}
impl<'a, F: FnMut(&str, &mut dyn IWrite)> IAction for Action<'a, F> {
fn invoke(&mut self, args: &str, writer: &mut dyn IWrite) {
(self.invoke)(args, writer)
}
}