calamity-commands: A library for declaring, parsing, and invoking text-input based commands

[ library, mit, utils ] [ Propose Tags ]

Please see the README on GitHub at https://github.com/simmsb/calamity#readme


[Skip to Readme]
Versions [RSS] [faq] 0.1.0.0, 0.1.1.0, 0.1.2.0, 0.1.3.0
Change log ChangeLog.md
Dependencies base (>=4.13 && <5), generic-lens (>=2.0 && <3), lens (>=4.18 && <6), megaparsec (>=8 && <10), polysemy (>=1.5 && <2), polysemy-plugin (==0.3.*), text (>=1.2 && <2), text-show (>=3.8 && <4), unordered-containers (==0.2.*) [details]
License MIT
Copyright 2020 Ben Simms
Author Ben Simms
Maintainer ben@bensimms.moe
Category Utils
Home page https://github.com/simmsb/calamity
Bug tracker https://github.com/simmsb/calamity/issues
Source repo head: git clone https://github.com/simmsb/calamity
Uploaded by nitros12 at 2021-05-26T16:52:28Z
Distributions NixOS:0.1.3.0
Downloads 369 total (27 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2021-05-26 [all 1 reports]

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

For package maintainers and hackage trustees

Candidates


Readme for calamity-commands-0.1.3.0

[back to package description]

Calamity Commands

Hackage Gitlab pipeline status License Hackage-Deps Discord Invite

Calamity Commands is a Haskell library for constructing text-based commands, it uses Polysemy as the core library for handling effects, allowing you to pick and choose how to handle certain features of the library.

Docs

You can find documentation on hackage at: https://hackage.haskell.org/package/calamity-commands

Examples

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLabels #-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}

{-# LANGUAGE TypeOperators #-}

module Main where

import Polysemy
import CalamityCommands.Commands
import CalamityCommands.Commands.Help
import CalamityCommands.Commands.Context
import CalamityCommands.Commands.ParsePrefix
import Data.Functor.Identity
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import qualified Data.Text.IO as T
import qualified Data.Text.Lazy.IO as LT

-- Make a command handler, we don't actually use the context and therefore this
-- handler is generic over the context used
h' :: CommandContext Identity c (Either LT.Text Int) => CommandHandler Identity c (Either LT.Text Int)
h' = runIdentity . runFinal $ do
  (h, _) <- buildCommands $ do
        command @'[Int, Int] "add" $ \ctx a b -> pure $ Right (a + b)
        command @'[Int, Int] "mul" $ \ctx a b -> pure $ Right (a * b)
        helpCommand (pure . Left)
  pure h

-- To use the commands we need to provide the interpreters for
-- 'ConstructContext' and 'ParsePrefix', the default provided ones are being
-- used here: 'useBasicContext' which makes @ctx ~ 'BasicContext'@, and
-- @'useConstantPrefix' "!"@ which treats any input starting with @!@ as a
-- command.
--
--
-- The 'processCommands' function can then be used to parse and invoke commands,
-- since commands are generic over the monad they run in we use @'runIdentity' .
-- 'runFinal' . 'embedToFinal'@ to have the commands interpreted purely.
--
--
-- This function 'r' takes an input string such as "!add 1 2", and then looks up
-- the invoked command and runs it, returning the result.
r :: LT.Text -> Maybe (Either
                       (CmdInvokeFailReason (BasicContext Identity (Either LT.Text Int)))
                       (BasicContext Identity (Either LT.Text Int), Either LT.Text Int))
r = runIdentity . runFinal . embedToFinal . useBasicContext . useConstantPrefix "!" . processCommands h'

-- Then to display the result of processing the command nicely, we can use a
-- something like this function, which prints the result of a command if one was
-- invoked successfully, and prints the error nicely if not.
rm :: LT.Text -> IO ()
rm s = case r s of
            Just (Right (_, Right r)) ->
              print r

            Just (Right (_, Left h)) ->
              LT.putStrLn h

            Just (Left (CommandInvokeError _ (ParseError t r))) ->
              LT.putStrLn ("Parsing parameter " <> LT.fromStrict t <> " failed with reason: " <> r)

            _ -> pure ()