From 5f330da4e4335fe92208052612d3e96e43c704b6 Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Fri, 3 Dec 2021 23:27:17 -0300 Subject: Begin CommandNode port --- src/args.rs | 73 +++++++++++++++++++++++++++++++++++------- src/lib.rs | 14 ++++++++ src/tree.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 12 deletions(-) create mode 100644 src/tree.rs diff --git a/src/args.rs b/src/args.rs index 7e84b48..2b8eecf 100644 --- a/src/args.rs +++ b/src/args.rs @@ -5,7 +5,9 @@ //! Argument processing. +use ::std::any::Any; use ::std::future::Future; +use ::std::io::Cursor; use ::std::pin::Pin; use crate::strcursor::StringReader; @@ -13,7 +15,8 @@ use crate::suggestion::Suggestions; use crate::suggestion::SuggestionsBuilder; // FIXME delete when implemented -pub struct CommandContext<'a, T>(::std::marker::PhantomData<(&'a str, T)>); +/// The parsing context of a command. +pub struct CommandContext<'i, S, E>(::std::marker::PhantomData<(&'i str, S, E)>); /// An argument parser. /// @@ -29,33 +32,39 @@ pub struct CommandContext<'a, T>(::std::marker::PhantomData<(&'a str, T)>); /// A very basic `bool` argument type: /// /// ``` +/// use ::std::io::Cursor; +/// /// use ::iosonism::args::ArgumentType; /// use ::iosonism::strcursor::{ReadError, StringReader}; /// /// struct BoolArgumentType; /// -/// impl<'i, R, S, E> ArgumentType<'i, R, S, E> for BoolArgumentType -/// where R: StringReader<'i>, E: ReadError<'i, R> { +/// impl ArgumentType for BoolArgumentType +/// where for<'i> E: ReadError<'i, Cursor<&'i str>> +/// { /// type Result = bool; -/// fn parse(&self, reader: &mut R) -> Result { +/// fn parse<'i>( +/// &self, +/// reader: &mut Cursor<&'i str>, +/// ) -> Result where E: 'i { /// reader.read_bool() /// } /// } /// ``` -pub trait ArgumentType<'i, R: StringReader<'i>, S, E> { +pub trait ArgumentType { /// The parsed type of the argument. - type Result: Sized + 'static + ::std::any::Any; + type Result: Sized + 'static + Any; /// Parses an argument of this type, returning the parsed argument. - fn parse(&self, reader: &mut R) -> Result; + fn parse<'i>( + &self, + reader: &mut Cursor<&'i str>, + ) -> Result where E: 'i; /// Creates suggestions for this argument. - // The hope here is that S is lightweight enough for one to clone into the - // closure. Unfortunately making it borrowable would be a pain. - // FIXME: this API doesn't look great, can we make it better somehow? - fn list_suggestions( + fn list_suggestions<'i>( &self, - context: &CommandContext<'i, S>, + context: &CommandContext<'i, S, E>, builder: SuggestionsBuilder<'i>, ) -> Pin + Send + 'i>> { let _ = context; @@ -68,3 +77,43 @@ pub trait ArgumentType<'i, R: StringReader<'i>, S, E> { Vec::new() } } + +/// Wrapper around `ArgumentType`, but with `Any`. +pub(crate) trait ArgumentTypeAny { + /// Parses an argument of this type, returning the parsed argument. + fn parse<'i>( + &self, + reader: &mut Cursor<&'i str>, + ) -> Result, E> where E: 'i; + + /// Creates suggestions for this argument. + fn list_suggestions<'i>( + &self, + context: &CommandContext<'i, S, E>, + builder: SuggestionsBuilder<'i>, + ) -> Pin + Send + 'i>>; + + /// Returns examples for this argument. + fn get_examples(&self) -> Vec<&str>; +} + +impl, S, E> ArgumentTypeAny for T { + fn parse<'i>( + &self, + reader: &mut Cursor<&'i str>, + ) -> Result, E> where E: 'i { + self.parse(reader).map(|x| Box::new(x) as _) + } + + fn list_suggestions<'i>( + &self, + context: &CommandContext<'i, S, E>, + builder: SuggestionsBuilder<'i>, + ) -> Pin + Send + 'i>> { + self.list_suggestions(context, builder) + } + + fn get_examples(&self) -> Vec<&str> { + self.get_examples() + } +} diff --git a/src/lib.rs b/src/lib.rs index 1557709..6921bbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,20 @@ pub mod strcursor; pub mod suggestion; pub mod args; +pub mod tree; + +use crate::args::CommandContext; + +/// Type of a command handler. +/// +/// # Type params +/// +/// - `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 = Box< + dyn for<'a, 'i> Fn(&'a CommandContext<'i, S, E>) -> Result + Send + Sync +>; #[cfg(test)] mod tests { diff --git a/src/tree.rs b/src/tree.rs new file mode 100644 index 0000000..b534754 --- /dev/null +++ b/src/tree.rs @@ -0,0 +1,105 @@ +// Copyright (c) 2021 Soni L. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +// Documentation and comments licensed under CC BY-SA 4.0. + +//! Command syntax tree. + +use ::std::borrow::Cow; + +use crate::Command; +use crate::args::{ArgumentType, ArgumentTypeAny}; +use crate::strcursor::StringReader; +// FIXME +use crate::args::CommandContext; + +/// The kind of node. +enum NodeKind { + /// Root node. + Root, + /// Literal node. + Literal { + /// The literal of the node. + literal: Cow<'static, str>, + }, + /// Argument node. + Argument { + /// The label of the node. + label: Cow<'static, str>, + /// The argument type. + arg: Box>, + }, +} + +/// A node in the syntax tree. +pub struct CommandNode { + /// The command to run for this node. + command: Option>, + /// Nodes. + nodes: (), + /// Literal nodes. + literals: (), + /// Argument nodes. + arguments: (), + /// The kind of node. + kind: NodeKind, +} + +impl CommandNode { + /// 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>, + ) -> Self { + CommandNode { + command: command, + nodes: (), + literals: (), + arguments: (), + kind: NodeKind::Literal { + literal: word, + }, + } + } + + /// Creates a new argument node. + pub fn argument + 'static>( + name: Cow<'static, str>, + command: Option>, + 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, + } + } +} -- cgit 1.4.1