calamity-commands 0.1.1.0 → 0.1.2.0
raw patch · 8 files changed
+236/−91 lines, 8 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- CalamityCommands: parserName :: ParameterParser a r => Text
- CalamityCommands.CommandUtils: instance (CalamityCommands.Parser.ParameterParser x r, CalamityCommands.CommandUtils.ParamNamesForParsers xs r) => CalamityCommands.CommandUtils.ParamNamesForParsers (x : xs) r
- CalamityCommands.CommandUtils: instance forall k (r :: k). CalamityCommands.CommandUtils.ParamNamesForParsers '[] r
- CalamityCommands.Parser: instance (CalamityCommands.Parser.ParameterParser a r, CalamityCommands.Parser.ParameterParser b r) => CalamityCommands.Parser.ParameterParser (Data.Either.Either a b) r
- CalamityCommands.Parser: instance (CalamityCommands.Parser.ParameterParser a r, CalamityCommands.Parser.ParameterParser b r) => CalamityCommands.Parser.ParameterParser (a, b) r
- CalamityCommands.Parser: instance (CalamityCommands.Parser.ParameterParser a r, Data.Typeable.Internal.Typeable a) => CalamityCommands.Parser.ParameterParser (GHC.Base.NonEmpty a) r
- CalamityCommands.Parser: instance (GHC.Base.Monoid (CalamityCommands.Parser.ParserResult a), CalamityCommands.Parser.ParameterParser a r) => CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleeneStarConcat a) r
- CalamityCommands.Parser: instance (GHC.Base.Semigroup (CalamityCommands.Parser.ParserResult a), CalamityCommands.Parser.ParameterParser a r) => CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleenePlusConcat a) r
- CalamityCommands.Parser: instance (GHC.TypeLits.KnownSymbol s, CalamityCommands.Parser.ParameterParser a r) => CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.Named s a) r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser () r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleenePlusConcat Data.Text.Internal.Lazy.Text) r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleenePlusConcat Data.Text.Internal.Text) r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleeneStarConcat Data.Text.Internal.Lazy.Text) r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleeneStarConcat Data.Text.Internal.Text) r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser Data.Text.Internal.Lazy.Text r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser Data.Text.Internal.Text r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Integer.Type.Integer r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Natural.Natural r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Types.Float r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Types.Int r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Types.Word r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser a r => CalamityCommands.Parser.ParameterParser (GHC.Maybe.Maybe a) r
- CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser a r => CalamityCommands.Parser.ParameterParser [a] r
- CalamityCommands.Parser: parserName :: ParameterParser a r => Text
+ CalamityCommands: parameterDescription :: ParameterParser a c r => Text
+ CalamityCommands: parameterInfo :: ParameterParser a c r => ParameterInfo
+ CalamityCommands.CommandUtils: instance (CalamityCommands.Parser.ParameterParser x c r, CalamityCommands.CommandUtils.ParameterInfoForParsers xs c r) => CalamityCommands.CommandUtils.ParameterInfoForParsers (x : xs) c r
+ CalamityCommands.CommandUtils: instance forall k1 k2 (c :: k1) (r :: k2). CalamityCommands.CommandUtils.ParameterInfoForParsers '[] c r
+ CalamityCommands.ParameterInfo: ParameterInfo :: Maybe Text -> TypeRep -> Text -> ParameterInfo
+ CalamityCommands.ParameterInfo: [$sel:name:ParameterInfo] :: ParameterInfo -> Maybe Text
+ CalamityCommands.ParameterInfo: [$sel:typeDescription:ParameterInfo] :: ParameterInfo -> Text
+ CalamityCommands.ParameterInfo: [$sel:type_:ParameterInfo] :: ParameterInfo -> TypeRep
+ CalamityCommands.ParameterInfo: data ParameterInfo
+ CalamityCommands.ParameterInfo: instance GHC.Generics.Generic CalamityCommands.ParameterInfo.ParameterInfo
+ CalamityCommands.ParameterInfo: instance GHC.Show.Show CalamityCommands.ParameterInfo.ParameterInfo
+ CalamityCommands.ParameterInfo: instance TextShow.Classes.TextShow CalamityCommands.ParameterInfo.ParameterInfo
+ CalamityCommands.Parser: SpannedError :: Text -> !Int -> !Int -> SpannedError
+ CalamityCommands.Parser: data SpannedError
+ CalamityCommands.Parser: instance (CalamityCommands.Parser.ParameterParser a c r, CalamityCommands.Parser.ParameterParser b c r) => CalamityCommands.Parser.ParameterParser (Data.Either.Either a b) c r
+ CalamityCommands.Parser: instance (CalamityCommands.Parser.ParameterParser a c r, CalamityCommands.Parser.ParameterParser b c r) => CalamityCommands.Parser.ParameterParser (a, b) c r
+ CalamityCommands.Parser: instance (CalamityCommands.Parser.ParameterParser a c r, Data.Typeable.Internal.Typeable a) => CalamityCommands.Parser.ParameterParser (GHC.Base.NonEmpty a) c r
+ CalamityCommands.Parser: instance (GHC.Base.Monoid (CalamityCommands.Parser.ParserResult a), CalamityCommands.Parser.ParameterParser a c r) => CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleeneStarConcat a) c r
+ CalamityCommands.Parser: instance (GHC.Base.Semigroup (CalamityCommands.Parser.ParserResult a), CalamityCommands.Parser.ParameterParser a c r) => CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleenePlusConcat a) c r
+ CalamityCommands.Parser: instance (GHC.TypeLits.KnownSymbol s, CalamityCommands.Parser.ParameterParser a c r) => CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.Named s a) c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser () c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleenePlusConcat Data.Text.Internal.Lazy.Text) c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleenePlusConcat Data.Text.Internal.Text) c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleeneStarConcat Data.Text.Internal.Lazy.Text) c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser (CalamityCommands.Parser.KleeneStarConcat Data.Text.Internal.Text) c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser Data.Text.Internal.Lazy.Text c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser Data.Text.Internal.Text c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Integer.Type.Integer c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Natural.Natural c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Types.Float c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Types.Int c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser GHC.Types.Word c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser a c r => CalamityCommands.Parser.ParameterParser (GHC.Maybe.Maybe a) c r
+ CalamityCommands.Parser: instance CalamityCommands.Parser.ParameterParser a c r => CalamityCommands.Parser.ParameterParser [a] c r
+ CalamityCommands.Parser: parameterDescription :: ParameterParser a c r => Text
+ CalamityCommands.Parser: parameterInfo :: ParameterParser a c r => ParameterInfo
- CalamityCommands: class Typeable a => ParameterParser (a :: Type) r where {
+ CalamityCommands: class Typeable a => ParameterParser (a :: Type) c r where {
- CalamityCommands: parse :: ParameterParser a r => Sem (ParserEffs c r) (ParserResult a)
+ CalamityCommands: parse :: ParameterParser a c r => Sem (ParserEffs c r) (ParserResult a)
- CalamityCommands.Command: Command :: NonEmpty Text -> Maybe (Group m c a) -> Bool -> [Check m c] -> [Text] -> (c -> Text) -> (c -> m (Either CommandError p)) -> ((c, p) -> m (Either Text a)) -> Command (m :: Type -> Type) (c :: Type) (a :: Type)
+ CalamityCommands.Command: Command :: NonEmpty Text -> Maybe (Group m c a) -> Bool -> [Check m c] -> [ParameterInfo] -> (c -> Text) -> (c -> m (Either CommandError p)) -> ((c, p) -> m (Either Text a)) -> Command (m :: Type -> Type) (c :: Type) (a :: Type)
- CalamityCommands.Command: [$sel:params:Command] :: Command (m :: Type -> Type) (c :: Type) (a :: Type) -> [Text]
+ CalamityCommands.Command: [$sel:params:Command] :: Command (m :: Type -> Type) (c :: Type) (a :: Type) -> [ParameterInfo]
- CalamityCommands.CommandUtils: buildCommand :: forall ps c m a r. (Monad m, Member (Final m) r, TypedCommandC ps a r, CommandContext m c a) => NonEmpty Text -> Maybe (Group m c a) -> Bool -> [Check m c] -> (c -> Text) -> (c -> CommandForParsers ps r a) -> Sem r (Command m c a)
+ CalamityCommands.CommandUtils: buildCommand :: forall ps c m a r. (Monad m, Member (Final m) r, TypedCommandC ps c a r, CommandContext m c a) => NonEmpty Text -> Maybe (Group m c a) -> Bool -> [Check m c] -> (c -> Text) -> (c -> CommandForParsers ps r a) -> Sem r (Command m c a)
- CalamityCommands.CommandUtils: buildCommand' :: forall c m a p r. (Monad m, Member (Final m) r) => NonEmpty Text -> Maybe (Group m c a) -> Bool -> [Check m c] -> [Text] -> (c -> Text) -> (c -> Sem r (Either CommandError p)) -> ((c, p) -> Sem (Fail : r) a) -> Sem r (Command m c a)
+ CalamityCommands.CommandUtils: buildCommand' :: forall c m a p r. (Monad m, Member (Final m) r) => NonEmpty Text -> Maybe (Group m c a) -> Bool -> [Check m c] -> [ParameterInfo] -> (c -> Text) -> (c -> Sem r (Either CommandError p)) -> ((c, p) -> Sem (Fail : r) a) -> Sem r (Command m c a)
- CalamityCommands.CommandUtils: type TypedCommandC ps a r = (ApplyTupRes (ParserResult (ListToTup ps)) (CommandSemType r a) ~ CommandForParsers ps r a, ParameterParser (ListToTup ps) r, ApplyTup (ParserResult (ListToTup ps)) (CommandSemType r a), ParamNamesForParsers ps r)
+ CalamityCommands.CommandUtils: type TypedCommandC ps c a r = (ApplyTupRes (ParserResult (ListToTup ps)) (CommandSemType r a) ~ CommandForParsers ps r a, ParameterParser (ListToTup ps) c r, ApplyTup (ParserResult (ListToTup ps)) (CommandSemType r a), ParameterInfoForParsers ps c r)
- CalamityCommands.Context: constructContext :: forall msg_aPlQ ctx_aPlO m'_aPlN a'_aPlP r_aPor. MemberWithError (ConstructContext msg_aPlQ ctx_aPlO m'_aPlN a'_aPlP) r_aPor => (Text, Command m'_aPlN ctx_aPlO a'_aPlP, Text) -> msg_aPlQ -> Sem r_aPor (Maybe ctx_aPlO)
+ CalamityCommands.Context: constructContext :: forall msg_aSny ctx_aSnw m'_aSnv a'_aSnx r_aSq9. MemberWithError (ConstructContext msg_aSny ctx_aSnw m'_aSnv a'_aSnx) r_aSq9 => (Text, Command m'_aSnv ctx_aSnw a'_aSnx, Text) -> msg_aSny -> Sem r_aSq9 (Maybe ctx_aSnw)
- CalamityCommands.Dsl: command :: forall ps c a m r. (Monad m, Member (Final m) r, TypedCommandC ps a r, CommandContext m c a) => Text -> (c -> CommandForParsers ps r a) -> Sem (DSLState m c a r) (Command m c a)
+ CalamityCommands.Dsl: command :: forall ps c a m r. (Monad m, Member (Final m) r, TypedCommandC ps c a r, CommandContext m c a) => Text -> (c -> CommandForParsers ps r a) -> Sem (DSLState m c a r) (Command m c a)
- CalamityCommands.Dsl: command' :: (Monad m, Member (Final m) r) => Text -> [Text] -> (c -> Sem r (Either CommandError p)) -> ((c, p) -> Sem (Fail : r) a) -> Sem (DSLState m c a r) (Command m c a)
+ CalamityCommands.Dsl: command' :: (Monad m, Member (Final m) r) => Text -> [ParameterInfo] -> (c -> Sem r (Either CommandError p)) -> ((c, p) -> Sem (Fail : r) a) -> Sem (DSLState m c a r) (Command m c a)
- CalamityCommands.Dsl: commandA :: forall ps c a m r. (Monad m, Member (Final m) r, TypedCommandC ps a r, CommandContext m c a) => Text -> [Text] -> (c -> CommandForParsers ps r a) -> Sem (DSLState m c a r) (Command m c a)
+ CalamityCommands.Dsl: commandA :: forall ps c a m r. (Monad m, Member (Final m) r, TypedCommandC ps c a r, CommandContext m c a) => Text -> [Text] -> (c -> CommandForParsers ps r a) -> Sem (DSLState m c a r) (Command m c a)
- CalamityCommands.Dsl: commandA' :: forall p c a m r. (Monad m, Member (Final m) r) => Text -> [Text] -> [Text] -> (c -> Sem r (Either CommandError p)) -> ((c, p) -> Sem (Fail : r) a) -> Sem (DSLState m c a r) (Command m c a)
+ CalamityCommands.Dsl: commandA' :: forall p c a m r. (Monad m, Member (Final m) r) => Text -> [Text] -> [ParameterInfo] -> (c -> Sem r (Either CommandError p)) -> ((c, p) -> Sem (Fail : r) a) -> Sem (DSLState m c a r) (Command m c a)
- CalamityCommands.Help: helpCommand :: forall c m a r. (Monad m, Member (Final m) r, CommandContext m c a) => (Text -> Sem (Fail : r) a) -> Sem (DSLState m c a r) (Command m c a)
+ CalamityCommands.Help: helpCommand :: forall c m a r. (Monad m, Member (Final m) r, CommandContext m c a) => (c -> Text -> Sem (Fail : r) a) -> Sem (DSLState m c a r) (Command m c a)
- CalamityCommands.Help: helpCommand' :: (Monad m, Member (Final m) r, CommandContext m c a) => CommandHandler m c a -> Maybe (Group m c a) -> [Check m c] -> (Text -> Sem (Fail : r) a) -> Sem r (Command m c a)
+ CalamityCommands.Help: helpCommand' :: (Monad m, Member (Final m) r, CommandContext m c a) => CommandHandler m c a -> Maybe (Group m c a) -> [Check m c] -> (c -> Text -> Sem (Fail : r) a) -> Sem r (Command m c a)
- CalamityCommands.ParsePrefix: parsePrefix :: forall msg_aQaC r_aQbs. MemberWithError (ParsePrefix msg_aQaC) r_aQbs => msg_aQaC -> Sem r_aQbs (Maybe (Text, Text))
+ CalamityCommands.ParsePrefix: parsePrefix :: forall msg_aTck r_aTda. MemberWithError (ParsePrefix msg_aTck) r_aTda => msg_aTck -> Sem r_aTda (Maybe (Text, Text))
- CalamityCommands.Parser: class Typeable a => ParameterParser (a :: Type) r where {
+ CalamityCommands.Parser: class Typeable a => ParameterParser (a :: Type) c r where {
- CalamityCommands.Parser: parse :: ParameterParser a r => Sem (ParserEffs c r) (ParserResult a)
+ CalamityCommands.Parser: parse :: ParameterParser a c r => Sem (ParserEffs c r) (ParserResult a)
- CalamityCommands.Parser: parseMP :: Text -> ParsecT SpannedError Text (Sem (ParserCtxE c r)) a -> Sem (ParserEffs c r) a
+ CalamityCommands.Parser: parseMP :: Text -> ParsecT SpannedError Text (Sem (Reader c : r)) a -> Sem (ParserEffs c r) a
Files
- CalamityCommands/Command.hs +4/−4
- CalamityCommands/CommandUtils.hs +29/−21
- CalamityCommands/Dsl.hs +7/−6
- CalamityCommands/Help.hs +15/−9
- CalamityCommands/ParameterInfo.hs +19/−0
- CalamityCommands/Parser.hs +85/−43
- ChangeLog.md +18/−0
- calamity-commands.cabal +59/−8
CalamityCommands/Command.hs view
@@ -6,6 +6,7 @@ import CalamityCommands.Check import CalamityCommands.Error import CalamityCommands.Group+import CalamityCommands.ParameterInfo import Control.Lens hiding (Context, (<.>)) @@ -29,9 +30,8 @@ hidden :: Bool , -- | A list of checks that must pass for this command to be invoked checks :: [Check m c]- , -- | A list of the parameters the command takes, only used for constructing- -- help messages.- params :: [S.Text]+ , -- | A list of parameter metadata+ params :: [ParameterInfo] , -- | A function producing the \'help\' for the command. help :: c -> L.Text , -- | A function that parses the context for the command, producing the input@@ -44,7 +44,7 @@ data CommandS = CommandS { names :: NonEmpty S.Text- , params :: [S.Text]+ , params :: [ParameterInfo] , parent :: Maybe S.Text , checks :: [S.Text] , hidden :: Bool
CalamityCommands/CommandUtils.hs view
@@ -20,6 +20,7 @@ import CalamityCommands.Group import CalamityCommands.Internal.RunIntoM import CalamityCommands.Internal.Utils+import CalamityCommands.ParameterInfo import CalamityCommands.Parser import Control.Lens hiding (Context, (<.>))@@ -30,8 +31,8 @@ import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE import Data.Maybe-import Data.Text as S-import Data.Text.Lazy as L+import qualified Data.Text as S+import qualified Data.Text.Lazy as L import qualified Polysemy as P import qualified Polysemy.Error as P@@ -45,7 +46,14 @@ -- | Format a command's parameters commandParams :: Command m c a -> L.Text-commandParams Command{params} = L.fromStrict $ S.intercalate ", " params+commandParams Command{params} =+ let formatted =+ map+ ( \(ParameterInfo (fromMaybe "" -> name) type_ _) ->+ "`" <> name <> ":" <> S.pack (show type_) <> "`"+ )+ params+ in L.fromStrict $ S.intercalate ", " formatted {- | Given the properties of a 'Command' with the @parser@ and @callback@ in the 'P.Sem' monad, build a command by transforming the Polysemy actions into @m@@@ -62,8 +70,8 @@ Bool -> -- | The checks for the command [Check m c] ->- -- | The names of the command's parameters- [S.Text] ->+ -- | The command's parameter metadata+ [ParameterInfo] -> -- | The help generator for this command (c -> L.Text) -> -- | The parser for this command@@ -92,7 +100,7 @@ -} buildCommand :: forall ps c m a r.- (Monad m, P.Member (P.Final m) r, TypedCommandC ps a r, CommandContext m c a) =>+ (Monad m, P.Member (P.Final m) r, TypedCommandC ps c a r, CommandContext m c a) => -- | The name (and aliases) of the command NonEmpty S.Text -> -- | The parent group of the command@@ -108,7 +116,7 @@ P.Sem r (Command m c a) buildCommand names parent hidden checks help command = let (parser, cb) = buildTypedCommand @ps command- in buildCommand' names parent hidden checks (paramNames @ps @r) help parser cb+ in buildCommand' names parent hidden checks (parameterInfos @ps @r) help parser cb {- | Given the name of the command the parser is for and a parser function in the 'P.Sem' monad, build a parser by transforming the Polysemy action into an@@ -130,7 +138,7 @@ buildCallback :: (Monad m, P.Member (P.Final m) r) => ((c, p) -> P.Sem (P.Fail ': r) a) -> P.Sem r ((c, p) -> m (Either L.Text a)) buildCallback cb = do- cb' <- bindSemToM ( \x -> P.runFail (cb x) <&> mapLeft L.pack)+ cb' <- bindSemToM (\x -> P.runFail (cb x) <&> mapLeft L.pack) let cb'' = fromMaybe (Left "failed internally") <.> cb' pure cb'' @@ -152,16 +160,16 @@ type CommandSemType r a = P.Sem (P.Fail ': r) a -- | Some constraints used for making parameter typed commands work-type TypedCommandC ps a r =+type TypedCommandC ps c a r = ( ApplyTupRes (ParserResult (ListToTup ps)) (CommandSemType r a) ~ CommandForParsers ps r a- , ParameterParser (ListToTup ps) r+ , ParameterParser (ListToTup ps) c r , ApplyTup (ParserResult (ListToTup ps)) (CommandSemType r a)- , ParamNamesForParsers ps r+ , ParameterInfoForParsers ps r ) buildTypedCommand :: forall (ps :: [Type]) c m a p r.- (TypedCommandC ps a r, p ~ ParserResult (ListToTup ps), CommandContext m c a) =>+ (TypedCommandC ps c a r, p ~ ParserResult (ListToTup ps), CommandContext m c a) => (c -> CommandForParsers ps r a) -> ( c -> P.Sem r (Either CommandError p) , (c, p) -> P.Sem (P.Fail ': r) a@@ -171,14 +179,14 @@ consumer (ctx, r) = applyTup (cmd ctx) r in (parser, consumer) -class ParamNamesForParsers (ps :: [Type]) r where- paramNames :: [S.Text]+class ParameterInfoForParsers (ps :: [Type]) r where+ parameterInfos :: [ParameterInfo] -instance ParamNamesForParsers '[] r where- paramNames = []+instance ParameterInfoForParsers '[] r where+ parameterInfos = [] -instance (ParameterParser x r, ParamNamesForParsers xs r) => ParamNamesForParsers (x : xs) r where- paramNames = parserName @x @r : paramNames @xs @r+instance (ParameterParser x c r, ParameterInfoForParsers xs r) => ParameterInfoForParsers (x : xs) r where+ parameterInfos = parameterInfo @x @c @r : parameterInfos @xs @r class ApplyTup a b where type ApplyTupRes a b@@ -197,12 +205,12 @@ buildTypedCommandParser :: forall (ps :: [Type]) c r.- ParameterParser (ListToTup ps) r =>+ ParameterParser (ListToTup ps) c r => c -> L.Text -> P.Sem r (Either CommandError (ParserResult (ListToTup ps))) buildTypedCommandParser ctx t =- runCommandParser ctx t (parse @(ListToTup ps) @r) <&> \case+ runCommandParser ctx t (parse @(ListToTup ps) @c @r) <&> \case Right r -> Right r Left (n, e) -> Left $ ParseError n e @@ -210,7 +218,7 @@ ListToTup '[] = () ListToTup (x ': xs) = (x, ListToTup xs) -{- | Transform a type level list of types implementing the 'Parser' typeclass into+{- | Transform a type level list of types implementing the 'ParameterParser' typeclass into the type a command callback matching those parameters should be. As an example:
CalamityCommands/Dsl.hs view
@@ -30,6 +30,7 @@ import CalamityCommands.Error import CalamityCommands.Group hiding (help) import CalamityCommands.Handler+import CalamityCommands.ParameterInfo import CalamityCommands.Internal.LocalWriter import qualified Data.HashMap.Lazy as LH@@ -103,8 +104,8 @@ (Monad m, P.Member (P.Final m) r) => -- | The name of the command S.Text ->- -- | The names of the command's parameters- [S.Text] ->+ -- | The command's parameter metadata+ [ParameterInfo] -> -- | The parser for this command (c -> P.Sem r (Either CommandError p)) -> -- | The callback for this command@@ -126,8 +127,8 @@ S.Text -> -- | The aliases for the command [S.Text] ->- -- | The names of the command's parameters- [S.Text] ->+ -- | The command's parameter metadata+ [ParameterInfo] -> -- | The parser for this command (c -> P.Sem r (Either CommandError p)) -> -- | The callback for this command@@ -167,7 +168,7 @@ forall ps c a m r. ( Monad m , P.Member (P.Final m) r- , TypedCommandC ps a r+ , TypedCommandC ps c a r , CommandContext m c a ) => -- | The name of the command@@ -197,7 +198,7 @@ forall ps c a m r. ( Monad m , P.Member (P.Final m) r- , TypedCommandC ps a r+ , TypedCommandC ps c a r , CommandContext m c a ) => -- | The name of the command
CalamityCommands/Help.hs view
@@ -12,6 +12,7 @@ import CalamityCommands.Dsl import CalamityCommands.Group import CalamityCommands.Handler+import CalamityCommands.ParameterInfo import CalamityCommands.Internal.LocalWriter import Control.Applicative@@ -32,16 +33,22 @@ = Command' (Command m c a) | Group' (Group m c a) [S.Text] +parameterTypeHelp :: [ParameterInfo] -> L.Text+parameterTypeHelp pinfo =+ let dedup = LH.toList . LH.fromList $ map (\(ParameterInfo _ t d) -> (t, d)) pinfo+ typeDescs = L.unlines ["- " <> L.pack (show t) <> ": " <> L.fromStrict d | (t, d) <- dedup]+ in "Types:\n" <> typeDescs <> "\n"+ helpCommandHelp :: c -> L.Text helpCommandHelp _ = "Show help for a command or group." helpForCommand :: CommandContext m c a => c -> Command m c a -> L.Text-helpForCommand ctx cmd@Command{names, checks, help} =- "```\nUsage: " <> prefix' <> path' <> " " <> params' <> "\n"+helpForCommand ctx cmd@Command{names, checks, help, params} =+ "Usage: " <> prefix' <> path' <> " " <> params' <> "\n" <> aliasesFmt <> checksFmt+ <> parameterTypeHelp params <> help ctx- <> "\n```" where prefix' = ctxPrefix ctx path' = L.unwords . map L.fromStrict $ commandPath cmd@@ -81,14 +88,13 @@ helpForGroup :: CommandContext m c a => c -> Group m c a -> L.Text helpForGroup ctx grp =- "```\nGroup: " <> path' <> "\n"+ "Group: " <> path' <> "\n" <> aliasesFmt <> checksFmt <> (grp ^. #help) ctx <> "\n" <> groupsMsg <> commandsMsg- <> "\n```" where path' = L.fromStrict . S.unwords $ groupPath grp groups = onlyVisibleG . onlyOriginals . LH.elems $ grp ^. #children@@ -114,7 +120,7 @@ else "Checks: " <> L.unwords checks' <> "\n\n" rootHelp :: CommandHandler m c a -> L.Text-rootHelp handler = "```\n" <> groupsMsg <> commandsMsg <> "\n```"+rootHelp handler = groupsMsg <> commandsMsg where groups = onlyVisibleG . onlyOriginals . LH.elems $ handler ^. #groups commands = onlyVisibleC . onlyOriginals . LH.elems $ handler ^. #commands@@ -155,7 +161,7 @@ CommandHandler m c a -> Maybe (Group m c a) -> [Check m c] ->- (L.Text -> P.Sem (P.Fail ': r) a) ->+ (c -> L.Text -> P.Sem (P.Fail ': r) a) -> P.Sem r (Command m c a) helpCommand' handler parent checks render = buildCommand @'[[S.Text]]@@ -164,7 +170,7 @@ False checks helpCommandHelp- (\ctx path -> render $ renderHelp handler ctx path)+ (\ctx path -> render ctx $ renderHelp handler ctx path) {- | Create and register the default help command for all the commands registered in the commands DSL this is used in. The @render@ parameter is used so you can@@ -217,7 +223,7 @@ helpCommand :: forall c m a r. (Monad m, P.Member (P.Final m) r, CommandContext m c a) =>- (L.Text -> P.Sem (P.Fail ': r) a) ->+ (c -> L.Text -> P.Sem (P.Fail ': r) a) -> P.Sem (DSLState m c a r) (Command m c a) helpCommand render = do handler <- P.ask @(CommandHandler m c a)
+ CalamityCommands/ParameterInfo.hs view
@@ -0,0 +1,19 @@+-- | Parameter info for a command+module CalamityCommands.ParameterInfo (+ ParameterInfo (..),+) where++import qualified Data.Text as S+import Data.Typeable++import GHC.Generics (Generic)+import TextShow+import qualified TextShow.Generic as TSG++data ParameterInfo = ParameterInfo+ { name :: Maybe S.Text+ , type_ :: TypeRep+ , typeDescription :: S.Text+ }+ deriving (Show, Generic)+ deriving (TextShow) via TSG.FromGeneric ParameterInfo
CalamityCommands/Parser.hs view
@@ -4,12 +4,17 @@ Named, KleeneStarConcat, KleenePlusConcat,++ -- * Parameter parsing utilities ParserEffs, runCommandParser, ParserState (..), parseMP,+ SpannedError (..), ) where +import CalamityCommands.ParameterInfo+ import Control.Lens hiding (Context) import Control.Monad @@ -17,6 +22,7 @@ import Data.Generics.Labels () import Data.Kind import Data.List.NonEmpty (NonEmpty (..))+import Data.Maybe (fromMaybe) import Data.Semigroup import qualified Data.Text as S import qualified Data.Text.Lazy as L@@ -38,9 +44,6 @@ data SpannedError = SpannedError L.Text !Int !Int deriving (Show, Eq, Ord) -showTypeOf :: forall a. Typeable a => String-showTypeOf = show . typeRep $ Proxy @a- {- | The current state of the parser, used so that the entire remaining input is available. @@ -56,32 +59,35 @@ } deriving (Show, Generic) +-- | type ParserEffs c r = ( P.State ParserState- ': P.Error (S.Text, L.Text) -- the current parser state- ': P.Reader c -- (failing parser name, error reason)+ ': P.Error (S.Text, L.Text) -- (failing parser name, error reason)+ ': P.Reader c -- the current parser state ': r -- context )-type ParserCtxE c r = P.Reader c ': r -- | Run a command parser, @ctx@ is the context, @t@ is the text input runCommandParser :: c -> L.Text -> P.Sem (ParserEffs c r) a -> P.Sem r (Either (S.Text, L.Text) a) runCommandParser ctx t = P.runReader ctx . P.runError . P.evalState (ParserState 0 t) --- | A typeclass for things that can be parsed as parameters to commands.------ Any type that is an instance of ParamerParser can be used in the type level--- parameter @ps@ of 'CalamityCommands.Dsl.command',--- 'CalamityCommands.CommandUtils.buildCommand', etc.-class Typeable a => ParameterParser (a :: Type) r where+{- | A typeclass for things that can be parsed as parameters to commands.++ Any type that is an instance of ParamerParser can be used in the type level+ parameter @ps@ of 'CalamityCommands.Dsl.command',+ 'CalamityCommands.CommandUtils.buildCommand', etc.+-}+class Typeable a => ParameterParser (a :: Type) c r where type ParserResult a type ParserResult a = a - parserName :: S.Text- default parserName :: S.Text- parserName = ":" <> S.pack (showTypeOf @a)+ parameterInfo :: ParameterInfo+ default parameterInfo :: ParameterInfo+ parameterInfo = ParameterInfo Nothing (typeRep $ Proxy @a) (parameterDescription @a @c @r) + parameterDescription :: S.Text+ parse :: P.Sem (ParserEffs c r) (ParserResult a) {- | A named parameter, used to attach the name @s@ to a type in the command's@@ -89,20 +95,35 @@ -} data Named (s :: Symbol) (a :: Type) -instance (KnownSymbol s, ParameterParser a r) => ParameterParser (Named s a) r where+instance (KnownSymbol s, ParameterParser a c r) => ParameterParser (Named s a) c r where type ParserResult (Named s a) = ParserResult a - parserName = (S.pack . symbolVal $ Proxy @s) <> parserName @a @r+ parameterInfo =+ let ParameterInfo _ type_ typeDescription = parameterInfo @a @c @r+ in ParameterInfo (Just . S.pack . symbolVal $ Proxy @s) type_ typeDescription - parse = mapE (_1 .~ parserName @(Named s a) @r) $ parse @a @r+ parameterDescription = parameterDescription @a @c @r + parse = mapE (_1 .~ parserName @(Named s a) @c @r) $ parse @a @c @r++parserName :: forall a c r. ParameterParser a c r => S.Text+parserName =+ let ParameterInfo (fromMaybe "" -> name) type_ _ = parameterInfo @a @c @r+ in name <> ":" <> S.pack (show type_)+ mapE :: P.Member (P.Error e) r => (e -> e) -> P.Sem r a -> P.Sem r a mapE f m = P.catch m (P.throw . f) --- | Parse a paremeter using a MegaParsec parser.------ On failure this constructs a nice-looking megaparsec error for the failed parameter.-parseMP :: S.Text -> ParsecT SpannedError L.Text (P.Sem (ParserCtxE c r)) a -> P.Sem (ParserEffs c r) a+{- | Parse a paremeter using a MegaParsec parser.++ On failure this constructs a nice-looking megaparsec error for the failed parameter.+-}+parseMP ::+ -- | The name of the parser+ S.Text ->+ -- | The megaparsec parser+ ParsecT SpannedError L.Text (P.Sem (P.Reader c ': r)) a ->+ P.Sem (ParserEffs c r) a parseMP n m = do s <- P.get res <- P.raise . P.raise $ runParserT (skipN (s ^. #off) *> trackOffsets (space *> m)) "" (s ^. #msg)@@ -112,43 +133,52 @@ pure a Left s -> P.throw (n, L.pack $ errorBundlePretty s) -instance ParameterParser L.Text r where+instance ParameterParser L.Text c r where parse = parseMP (parserName @L.Text) item+ parameterDescription = "word or quoted string" -instance ParameterParser S.Text r where+instance ParameterParser S.Text c r where parse = parseMP (parserName @S.Text) (L.toStrict <$> item)+ parameterDescription = "word or quoted string" -instance ParameterParser Integer r where+instance ParameterParser Integer c r where parse = parseMP (parserName @Integer) (signed mempty decimal)+ parameterDescription = "number" -instance ParameterParser Natural r where+instance ParameterParser Natural c r where parse = parseMP (parserName @Natural) decimal+ parameterDescription = "number" -instance ParameterParser Int r where+instance ParameterParser Int c r where parse = parseMP (parserName @Int) (signed mempty decimal)+ parameterDescription = "number" -instance ParameterParser Word r where+instance ParameterParser Word c r where parse = parseMP (parserName @Word) decimal+ parameterDescription = "number" -instance ParameterParser Float r where+instance ParameterParser Float c r where parse = parseMP (parserName @Float) (signed mempty (try float <|> decimal))+ parameterDescription = "number" -instance ParameterParser a r => ParameterParser (Maybe a) r where+instance ParameterParser a c r => ParameterParser (Maybe a) c r where type ParserResult (Maybe a) = Maybe (ParserResult a) parse = P.catch (Just <$> parse @a) (const $ pure Nothing)+ parameterDescription = "optional " <> parameterDescription @a @c @r -instance (ParameterParser a r, ParameterParser b r) => ParameterParser (Either a b) r where+instance (ParameterParser a c r, ParameterParser b c r) => ParameterParser (Either a b) c r where type ParserResult (Either a b) = Either (ParserResult a) (ParserResult b) parse = do- l <- parse @(Maybe a) @r+ l <- parse @(Maybe a) @c @r case l of Just l' -> pure (Left l') Nothing ->- Right <$> parse @b @r+ Right <$> parse @b @c @r+ parameterDescription = "either '" <> parameterDescription @a @c @r <> "' or '" <> parameterDescription @b @c @r <> "'" -instance ParameterParser a r => ParameterParser [a] r where+instance ParameterParser a c r => ParameterParser [a] c r where type ParserResult [a] = [ParserResult a] parse = go []@@ -159,7 +189,9 @@ Just a -> go $ l <> [a] Nothing -> pure l -instance (ParameterParser a r, Typeable a) => ParameterParser (NonEmpty a) r where+ parameterDescription = "zero or more '" <> parameterDescription @a @c @r <> "'"++instance (ParameterParser a c r, Typeable a) => ParameterParser (NonEmpty a) c r where type ParserResult (NonEmpty a) = NonEmpty (ParserResult a) parse = do@@ -167,28 +199,33 @@ as <- parse @[a] pure $ a :| as + parameterDescription = "one or more '" <> parameterDescription @a @c @r <> "'"+ {- | A parser that consumes zero or more of @a@ then concatenates them together. @'KleeneStarConcat' 'L.Text'@ therefore consumes all remaining input. -} data KleeneStarConcat (a :: Type) -instance (Monoid (ParserResult a), ParameterParser a r) => ParameterParser (KleeneStarConcat a) r where+instance (Monoid (ParserResult a), ParameterParser a c r) => ParameterParser (KleeneStarConcat a) c r where type ParserResult (KleeneStarConcat a) = ParserResult a parse = mconcat <$> parse @[a]+ parameterDescription = "zero or more '" <> parameterDescription @a @c @r <> "'" -instance {-# OVERLAPS #-} ParameterParser (KleeneStarConcat L.Text) r where+instance {-# OVERLAPS #-} ParameterParser (KleeneStarConcat L.Text) c r where type ParserResult (KleeneStarConcat L.Text) = ParserResult L.Text -- consume rest on text just takes everything remaining parse = parseMP (parserName @(KleeneStarConcat L.Text)) manySingle+ parameterDescription = "the remaining input" -instance {-# OVERLAPS #-} ParameterParser (KleeneStarConcat S.Text) r where+instance {-# OVERLAPS #-} ParameterParser (KleeneStarConcat S.Text) c r where type ParserResult (KleeneStarConcat S.Text) = ParserResult S.Text -- consume rest on text just takes everything remaining parse = parseMP (parserName @(KleeneStarConcat S.Text)) (L.toStrict <$> manySingle)+ parameterDescription = "the remaining input" {- | A parser that consumes one or more of @a@ then concatenates them together. @@ -196,33 +233,38 @@ -} data KleenePlusConcat (a :: Type) -instance (Semigroup (ParserResult a), ParameterParser a r) => ParameterParser (KleenePlusConcat a) r where+instance (Semigroup (ParserResult a), ParameterParser a c r) => ParameterParser (KleenePlusConcat a) c r where type ParserResult (KleenePlusConcat a) = ParserResult a parse = sconcat <$> parse @(NonEmpty a)+ parameterDescription = "one or more '" <> parameterDescription @a @c @r <> "'" -instance {-# OVERLAPS #-} ParameterParser (KleenePlusConcat L.Text) r where+instance {-# OVERLAPS #-} ParameterParser (KleenePlusConcat L.Text) c r where type ParserResult (KleenePlusConcat L.Text) = ParserResult L.Text -- consume rest on text just takes everything remaining parse = parseMP (parserName @(KleenePlusConcat L.Text)) someSingle+ parameterDescription = "the remaining input" -instance {-# OVERLAPS #-} ParameterParser (KleenePlusConcat S.Text) r where+instance {-# OVERLAPS #-} ParameterParser (KleenePlusConcat S.Text) c r where type ParserResult (KleenePlusConcat S.Text) = ParserResult S.Text -- consume rest on text just takes everything remaining parse = parseMP (parserName @(KleenePlusConcat S.Text)) (L.toStrict <$> someSingle)+ parameterDescription = "the remaining input" -instance (ParameterParser a r, ParameterParser b r) => ParameterParser (a, b) r where+instance (ParameterParser a c r, ParameterParser b c r) => ParameterParser (a, b) c r where type ParserResult (a, b) = (ParserResult a, ParserResult b) parse = do a <- parse @a b <- parse @b pure (a, b)+ parameterDescription = "'" <> parameterDescription @a @c @r <> "' then '" <> parameterDescription @b @c @r <> "'" -instance ParameterParser () r where+instance ParameterParser () c r where parse = parseMP (parserName @()) space+ parameterDescription = "whitespace" instance ShowErrorComponent SpannedError where showErrorComponent (SpannedError t _ _) = L.unpack t
ChangeLog.md view
@@ -1,5 +1,23 @@ # Changelog for Calamity Commands +## 0.1.2.0+++ Changed the command parameter machinery to hold more info about parameters.++ Added a 'type cheatsheet' thing to command help in the default help command.++ The `render` parameter of `helpCommand` and `helpCommand'` is now passed the+ context.++ `ParameterParser` now has an extra type variable `c` to parameterise the+ context type.++## 0.1.1.0+++ The minimum version of `base` has been upped to `4.13` as the library fails to+ build on ghc-8.6++ The minimum version of `polysemy` has been upped to 1.5++ The upper bound of `lens` has been bumped to 6+ ## 0.1.0.0++Initial release of the library ## Unreleased changes
calamity-commands.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.18 --- This file has been generated from package.yaml by hpack version 0.33.0.+-- This file has been generated from package.yaml by hpack version 0.34.4. -- -- see: https://github.com/sol/hpack ----- hash: d17b097b7481b77c55ba13052f69950fec4cd0809ca7f49be4f52b1a6fc2ad28+-- hash: 962098e7be30e39345aaa5a511635824c3654c1f199212a575271f3d7e6f5c60 name: calamity-commands-version: 0.1.1.0+version: 0.1.2.0 synopsis: A library for declaring, parsing, and invoking text-input based commands description: Please see the README on GitHub at <https://github.com/simmsb/calamity#readme> category: Utils@@ -18,7 +18,8 @@ copyright: 2020 Ben Simms license: MIT license-file: LICENSE-tested-with: GHC == 8.10.4+tested-with:+ GHC == 8.10.4 build-type: Simple extra-source-files: README.md@@ -46,14 +47,64 @@ CalamityCommands.Internal.LocalWriter CalamityCommands.Internal.RunIntoM CalamityCommands.Internal.Utils+ CalamityCommands.ParameterInfo CalamityCommands.ParsePrefix CalamityCommands.Parser CalamityCommands.Utils other-modules: Paths_calamity_commands hs-source-dirs:- ./.- default-extensions: StrictData AllowAmbiguousTypes BlockArguments NoMonomorphismRestriction BangPatterns BinaryLiterals UndecidableInstances ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable DoAndIfThenElse EmptyDataDecls ExistentialQuantification FlexibleContexts FlexibleInstances FunctionalDependencies GADTs DerivingVia DerivingStrategies GeneralizedNewtypeDeriving StandaloneDeriving DeriveAnyClass InstanceSigs KindSignatures LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns OverloadedStrings OverloadedLabels PartialTypeSignatures PatternGuards PolyKinds RankNTypes RecordWildCards ScopedTypeVariables TupleSections TypeFamilies TypeSynonymInstances ViewPatterns DuplicateRecordFields TypeOperators TypeApplications RoleAnnotations+ ./+ default-extensions:+ StrictData+ AllowAmbiguousTypes+ BlockArguments+ NoMonomorphismRestriction+ BangPatterns+ BinaryLiterals+ UndecidableInstances+ ConstraintKinds+ DataKinds+ DefaultSignatures+ DeriveDataTypeable+ DeriveFoldable+ DeriveFunctor+ DeriveGeneric+ DeriveTraversable+ DoAndIfThenElse+ EmptyDataDecls+ ExistentialQuantification+ FlexibleContexts+ FlexibleInstances+ FunctionalDependencies+ GADTs+ DerivingVia+ DerivingStrategies+ GeneralizedNewtypeDeriving+ StandaloneDeriving+ DeriveAnyClass+ InstanceSigs+ KindSignatures+ LambdaCase+ MultiParamTypeClasses+ MultiWayIf+ NamedFieldPuns+ OverloadedStrings+ OverloadedLabels+ PartialTypeSignatures+ PatternGuards+ PolyKinds+ RankNTypes+ RecordWildCards+ ScopedTypeVariables+ TupleSections+ TypeFamilies+ TypeSynonymInstances+ ViewPatterns+ DuplicateRecordFields+ TypeOperators+ TypeApplications+ RoleAnnotations ghc-options: -fplugin=Polysemy.Plugin -funbox-strict-fields -Wall -fno-warn-name-shadowing build-depends: base >=4.13 && <5@@ -61,8 +112,8 @@ , lens >=4.18 && <6 , megaparsec >=8 && <10 , polysemy >=1.5 && <2- , polysemy-plugin >=0.3 && <0.4+ , polysemy-plugin ==0.3.* , text >=1.2 && <2 , text-show >=3.8 && <4- , unordered-containers >=0.2 && <0.3+ , unordered-containers ==0.2.* default-language: Haskell2010