summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml3
-rw-r--r--src/args.rs67
-rw-r--r--src/lib.rs8
-rw-r--r--src/tree.rs169
4 files changed, 165 insertions, 82 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c21b5d8..bbd897b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,3 +13,6 @@ homepage = "https://soniex2.github.io/ganarchy/project/139eede493cff4b24a253d2e7
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+#ordered-float = "2.0"
+#qcell = { version = "0.5", default-features = false, features = ["alloc"] }
+anycast = "1.0"
diff --git a/src/args.rs b/src/args.rs
index 5b9ce72..e7d76c3 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -10,6 +10,7 @@ use ::std::borrow::Cow;
 use ::std::fmt::Display;
 use ::std::fmt::Formatter;
 use ::std::future::Future;
+use ::std::hash::Hash;
 use ::std::io::Cursor;
 use ::std::marker::PhantomData;
 use ::std::num::ParseFloatError;
@@ -19,6 +20,9 @@ use ::std::ops::RangeBounds;
 use ::std::pin::Pin;
 use ::std::str::FromStr;
 
+use anycast::Anycast;
+//use ::ordered_float::OrderedFloat;
+
 use crate::error::RangeError;
 use crate::error::ReadError;
 use crate::strcursor::StringReader;
@@ -27,7 +31,7 @@ use crate::suggestion::SuggestionsBuilder;
 
 // FIXME delete when implemented
 /// The parsing context of a command.
-pub struct CommandContext<'i, S, E>(::std::marker::PhantomData<(&'i str, S, E)>);
+pub struct CommandContext<'i, S, E>(PhantomData<(&'i str, S, E)>);
 
 /// An argument parser/validator.
 ///
@@ -36,8 +40,8 @@ pub struct CommandContext<'i, S, E>(::std::marker::PhantomData<(&'i str, S, E)>)
 /// this trait. Nevertheless, Iosonism doesn't itself use threads, so a
 /// [workaround] can be used if one needs non-`Send + Sync` argument types.
 ///
-/// Additionally, argument types must be `Display`. This *is* reflected in this
-/// trait.
+/// Additionally, argument types must be `Display`, `Eq` and `Hash`. This *is*
+/// reflected in this trait.
 ///
 /// [workaround]: https://users.rust-lang.org/t/how-to-check-send-at-runtime-similar-to-how-refcell-checks-borrowing-at-runtime/68269
 ///
@@ -78,7 +82,7 @@ pub struct CommandContext<'i, S, E>(::std::marker::PhantomData<(&'i str, S, E)>)
 ///     }
 /// }
 /// ```
-pub trait ArgumentType<S, E>: Display {
+pub trait ArgumentType<S, E>: Display + Eq + ::std::hash::Hash {
     /// The parsed type of the argument.
     type Result: Sized + 'static + Any;
 
@@ -105,8 +109,8 @@ pub trait ArgumentType<S, E>: Display {
     }
 }
 
-/// Internal wrapper around `ArgumentType`, but with `Any`.
-pub(crate) trait ArgumentTypeAny<S, E>: Send + Sync + Display {
+/// Internal wrapper around `ArgumentType`, but with more `Any`.
+pub(crate) trait ArgumentTypeAny<S, E>: Send + Sync + Display + Anycast {
     /// Parses an argument of this type, returning the parsed argument.
     fn parse<'i>(
         &self,
@@ -122,11 +126,18 @@ pub(crate) trait ArgumentTypeAny<S, E>: Send + Sync + Display {
 
     /// Returns examples for this argument.
     fn get_examples(&self) -> Cow<'static, [&str]>;
+
+    /// Compares this `ArgumentType` with another.
+    fn dyn_eq(&self, other: &dyn ArgumentTypeAny<S, E>) -> bool;
+
+    /// Hashes this `ArgumentType`.
+    fn dyn_hash(&self, hasher: &mut dyn ::std::hash::Hasher);
 }
 
-/// Any `ArgumentType` that is also `Send` and `Sync` is an `ArgumentTypeAny`.
+/// Any `ArgumentType` that is also `Send`, `Sync`, `Eq` and `Hash` is an
+/// `ArgumentTypeAny`.
 impl<T, S, E> ArgumentTypeAny<S, E> for T
-where T: ArgumentType<S, E> + Send + Sync {
+where T: ArgumentType<S, E> + Send + Sync + Eq + ::std::hash::Hash + Any {
     fn parse<'i>(
         &self,
         reader: &mut Cursor<&'i str>,
@@ -145,6 +156,18 @@ where T: ArgumentType<S, E> + Send + Sync {
     fn get_examples(&self) -> Cow<'static, [&str]> {
         self.get_examples()
     }
+
+    fn dyn_eq(&self, other: &dyn ArgumentTypeAny<S, E>) -> bool {
+        match other.any_ref().downcast_ref() {
+            Some(other) => self == other,
+            None => false,
+        }
+    }
+
+    fn dyn_hash(&self, mut hasher: &mut dyn ::std::hash::Hasher) {
+        // rust-lang/rust#44015
+        self.hash(&mut hasher)
+    }
 }
 
 /// Any `dyn ArgumentTypeAny` (note the `dyn`!) is an `ArgumentType`.
@@ -171,6 +194,21 @@ impl<S, E> ArgumentType<S, E> for dyn ArgumentTypeAny<S, E> {
     }
 }
 
+impl<S, E> PartialEq for dyn ArgumentTypeAny<S, E> {
+    fn eq(&self, other: &Self) -> bool {
+        self.dyn_eq(other)
+    }
+}
+
+impl<S, E> Eq for dyn ArgumentTypeAny<S, E> {
+}
+
+impl<S, E> ::std::hash::Hash for dyn ArgumentTypeAny<S, E> {
+    fn hash<H: ::std::hash::Hasher>(&self, hasher: &mut H) {
+        self.dyn_hash(hasher as &mut dyn ::std::hash::Hasher)
+    }
+}
+
 /// A boolean argument.
 // FIXME add examples/expand docs
 #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Default)]
@@ -270,8 +308,8 @@ impl<S, E, T, R> ArgumentType<S, E> for IntegerArgumentType<T, R>
 where
     for<'i> E: ReadError<'i, Cursor<&'i str>>,
     for<'i> E: RangeError<'i, Cursor<&'i str>, T, R>,
-    R: RangeBounds<T>,
-    T: PartialOrd<T> + FromStr<Err=ParseIntError> + Any + Display,
+    R: RangeBounds<T> + Eq + Hash,
+    T: PartialOrd<T> + FromStr<Err=ParseIntError> + Any + Display + Eq + Hash,
 {
     /// An `IntegerArgumentType` parses an integer type.
     type Result = T;
@@ -299,8 +337,8 @@ where
 
 /// Formats this `IntegerArgumentType`.
 ///
-/// The resulting string follows the syntax `"integer(start,end)"`, with `start`
-/// and `end` being one of the below:
+/// The resulting string follows the syntax `"integer(start,end)"`, with
+/// `start` and `end` being one of the below:
 ///
 /// - `value` if the bound is inclusive.
 /// - `value*` if the bound is exclusive.
@@ -328,6 +366,7 @@ impl<T: Display, R: RangeBounds<T>> Display for IntegerArgumentType<T, R> {
     }
 }
 
+// FIXME implement Eq and Hash properly
 /// A float argument.
 #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Default)]
 pub struct FloatArgumentType<T, R: RangeBounds<T>> {
@@ -376,8 +415,8 @@ impl<S, E, T, R> ArgumentType<S, E> for FloatArgumentType<T, R>
 where
     for<'i> E: ReadError<'i, Cursor<&'i str>>,
     for<'i> E: RangeError<'i, Cursor<&'i str>, T, R>,
-    R: RangeBounds<T>,
-    T: PartialOrd<T> + FromStr<Err=ParseFloatError> + Any + Display,
+    R: RangeBounds<T> + Eq + Hash,
+    T: PartialOrd<T> + FromStr<Err=ParseFloatError> + Any + Display + Eq + Hash,
 {
     /// A `FloatArgumentType` parses a float type.
     type Result = T;
diff --git a/src/lib.rs b/src/lib.rs
index 1895244..116add0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,6 +25,8 @@ pub mod strcursor;
 pub mod suggestion;
 pub mod tree;
 
+use ::std::sync::Arc;
+
 use crate::args::CommandContext;
 
 /// Type of a command handler.
@@ -34,8 +36,10 @@ use crate::args::CommandContext;
 /// - `T`: The type returned by the command.
 /// - `S`: The source type accepted by this argument type.
 /// - `E`: The error type accepted by this argument type.
-pub type Command<T, S, E> = Box<
-    dyn for<'a, 'i> Fn(&'a CommandContext<'i, S, E>) -> Result<T, E> + Send + Sync
+pub type Command<Type, Source, Error> = Arc<
+    dyn for<'a, 'i> Fn(
+        &'a CommandContext<'i, Source, Error>
+    ) -> Result<Type, Error> + Send + Sync
 >;
 
 #[cfg(test)]
diff --git a/src/tree.rs b/src/tree.rs
index 71243be..ae408a8 100644
--- a/src/tree.rs
+++ b/src/tree.rs
@@ -6,100 +6,137 @@
 //! Command syntax tree.
 
 use ::std::borrow::Cow;
+use ::std::collections::HashMap;
+use ::std::collections::HashSet;
+use ::std::hash::Hash;
+use ::std::sync::Arc;
 
 use crate::Command;
 use crate::args::{ArgumentType, ArgumentTypeAny};
 use crate::strcursor::StringReader;
-// FIXME
+// FIXME move
 use crate::args::CommandContext;
 
 /// The kind of node.
-enum NodeKind<S, E> {
+enum NodeKind<'a, Source, Error> {
     /// Root node.
     Root,
     /// Literal node.
     Literal {
         /// The literal of the node.
-        literal: Cow<'static, str>,
+        literal: Cow<'a, str>,
     },
     /// Argument node.
     Argument {
         /// The label of the node.
-        label: Cow<'static, str>,
+        label: Cow<'a, str>,
         /// The argument type.
-        arg: Box<dyn ArgumentTypeAny<S, E>>,
+        arg: Arc<dyn ArgumentTypeAny<Source, Error>>,
     },
 }
 
-/// A node in the syntax tree.
-pub struct CommandNode<T, S, E> {
+impl<'a, Source, Error> Clone for NodeKind<'a, Source, Error> {
+    fn clone(&self) -> Self {
+        match self {
+            &Self::Root => Self::Root,
+            &Self::Literal {
+                ref literal,
+            } => Self::Literal {
+                literal: Cow::clone(literal),
+            },
+            &Self::Argument {
+                ref label,
+                ref arg,
+            } => Self::Argument {
+                label: Cow::clone(label),
+                arg: Arc::clone(arg),
+            },
+        }
+    }
+}
+
+/// A node of the command parse graph.
+pub struct CommandNode<'a, Type, Source, Error> {
     /// The command to run for this node.
-    command: Option<Command<T, S, E>>,
+    command: Option<Command<Type, Source, Error>>,
     /// Nodes.
-    nodes: (),
+    nodes: HashMap<Cow<'a, str>, Cow<'a, CommandNode<'a, Type, Source, Error>>>,
     /// Literal nodes.
-    literals: (),
+    literals: HashSet<Cow<'a, str>>,
     /// Argument nodes.
-    arguments: (),
+    arguments: HashSet<Cow<'a, str>>,
     /// The kind of node.
-    kind: NodeKind<S, E>,
+    kind: NodeKind<'a, Source, Error>,
 }
 
-impl<T, S, E> CommandNode<T, S, E> {
-    /// Creates a new root node.
-    pub fn root() -> Self {
-        CommandNode {
-            command: None,
-            nodes: (),
-            literals: (),
-            arguments: (),
-            kind: NodeKind::Root,
-        }
-    }
-
-    /// Creates a new literal node.
-    pub fn literal(
-        word: Cow<'static, str>,
-        command: Option<Command<T, S, E>>,
-    ) -> Self {
-        CommandNode {
-            command: command,
-            nodes: (),
-            literals: (),
-            arguments: (),
-            kind: NodeKind::Literal {
-                literal: word,
-            },
+impl<'a, Type, Source, Error> Clone for CommandNode<'a, Type, Source, Error> {
+    fn clone(&self) -> Self {
+        Self {
+            command: self.command.clone(),
+            nodes: self.nodes.clone(),
+            literals: self.literals.clone(),
+            arguments: self.arguments.clone(),
+            kind: self.kind.clone(),
         }
     }
+}
 
-    /// Creates a new argument node.
-    pub fn argument<A: ArgumentType<S, E> + 'static + Send + Sync>(
-        name: Cow<'static, str>,
-        command: Option<Command<T, S, E>>,
-        argument: A,
-    ) -> Self {
-        CommandNode {
-            command: command,
-            nodes: (),
-            literals: (),
-            arguments: (),
-            kind: NodeKind::Argument {
-                label: name,
-                arg: Box::new(argument),
-            },
-        }
-    }
 
-    /// Returns the name of this node.
-    ///
-    /// For literal nodes, this is the literal itself. For argument nodes, this
-    /// is the name of the argument.
-    pub fn get_name(&self) -> &str {
-        match self.kind {
-            NodeKind::Root => "",
-            NodeKind::Literal { ref literal, .. } => literal,
-            NodeKind::Argument { ref label, .. } => label,
-        }
-    }
-}
+//impl<T, S, E> CommandNode<T, S, E> {
+//    /// Creates a new root node.
+//    pub fn root() -> Self {
+//        CommandNode {
+//            command: None,
+//            nodes: (),
+//            literals: (),
+//            arguments: (),
+//            kind: NodeKind::Root,
+//        }
+//    }
+//
+//    /// Creates a new literal node.
+//    pub fn literal(
+//        word: Cow<'static, str>,
+//        command: Option<Command<T, S, E>>,
+//    ) -> Self {
+//        CommandNode {
+//            command: command,
+//            nodes: (),
+//            literals: (),
+//            arguments: (),
+//            kind: NodeKind::Literal {
+//                literal: word,
+//            },
+//        }
+//    }
+//
+//    /// Creates a new argument node.
+//    pub fn argument<A: ArgumentType<S, E> + 'static + Send + Sync + Eq + Hash>(
+//        name: Cow<'static, str>,
+//        command: Option<Command<T, S, E>>,
+//        argument: A,
+//    ) -> Self {
+//        CommandNode {
+//            command: command,
+//            nodes: (),
+//            literals: (),
+//            arguments: (),
+//            kind: NodeKind::Argument {
+//                label: name,
+//                arg: Box::new(argument),
+//            },
+//        }
+//    }
+//
+//    /// Returns the name of this node.
+//    ///
+//    /// For literal nodes, this is the literal itself. For argument nodes, this
+//    /// is the name of the argument.
+//    pub fn get_name(&self) -> &str {
+//        match self.kind {
+//            NodeKind::Root => "",
+//            NodeKind::Literal { ref literal, .. } => literal,
+//            NodeKind::Argument { ref label, .. } => label,
+//        }
+//    }
+//}