packages feed

reflex-libtelnet 0.1.0.0 → 0.2.0.0

raw patch · 4 files changed

+184/−186 lines, 4 filesdep −data-defaultdep −lensdep ~basedep ~bytestringdep ~dependent-map

Dependencies removed: data-default, lens

Dependency ranges changed: base, bytestring, dependent-map, dependent-sum, dependent-sum-template, libtelnet, reflex

Files

CHANGELOG.md view
@@ -1,5 +1,26 @@ # Revision history for reflex-libtelnet +## 0.2.0.0 -- 2022-05-21++* Remove `instance Reflex t => Default (TelnetConfig t)`; use+  `newTelnetConfig` instead.++* New instances:+  - `instance Generic (TelnetConfig t)`+  - `instance Generic (TelnetEvents t)`++* Remove the dependency on `lens`:+  - As GHC is now better at dealing with duplicate record fields, we+    can give them nice unprefixed names.+  - Use+    [`generic-lens`](https://hackage.haskell.org/package/generic-lens)+    or+    [`generic-optics`](https://hackage.haskell.org/package/generic-optics)+    to get lenses for your preferred flavour of optic. Make sure to+    use the `field' @"fieldName"` version (i.e. not `#fieldName` or+    un-primed `field`), otherwise GHC will struggle to infer the type+    of `t` when setting fields in `ReflexConfig t`.+ ## 0.1.0.0 -- 2019-12-03  * First version. Released on an unsuspecting world.
README.md view
@@ -14,17 +14,26 @@  1. Assemble a `config :: TelnetConfig t`, which holds the event    streams that feed into a telnet state tracker. The easiest way to-   do this is to call `def` (there is an `instance Reflex t => Default-   (TelnetConfig t)`) and overwrite the fields you care about using-   record updates or lenses. You will almost certainly want to-   overwrite `_cRecv` to be the stream of incoming data from a socket,-   and `_cSend` to be the stream of outgoing data from your application.+   do this is to call `newTelnetConfig` and overwrite the fields you+   care about using record updates or lenses. You will almost+   certainly want to overwrite `recv` to be the stream of incoming+   data from a socket, and `send` to be the stream of outgoing data+   from your application. +   * If you want to use lenses, use the+     [`generic-lens`](https://hackage.haskell.org/package/generic-lens)+     or+     [`generic-optics`](https://hackage.haskell.org/package/generic-optics)+     to get your preferred flavour of optic. Make sure to use the+     `field' @"fieldName"` version (i.e. not `#fieldName` or un-primed+     `field`), otherwise GHC will struggle to infer the type of `t`+     when setting fields in `ReflexConfig t`.+ 2. Call `telnet config`, and save the resulting `TelnetEvents t`. This    structure holds the output event streams from a single telnet state    tracker. -3. Wire the ouput events into your application.+3. Wire the output events into your application.  ## Other Resources 
reflex-libtelnet.cabal view
@@ -1,37 +1,42 @@-cabal-version:       2.2-name:                reflex-libtelnet-version:             0.1.0.0-synopsis:            Reflex bindings for libtelnet-description:         Wraps https://git.sr.ht/~jack/libtelnet-haskell , so you-                     can handle telnet data streams using reflex-                     'Reflex.Event's. See @README.md@ or-                     "Reflex.LibTelnet" to get started.-homepage:            https://git.sr.ht/~jack/reflex-libtelnet-bug-reports:         https://todo.sr.ht/~jack/reflex-libtelnet-license:             GPL-3.0-or-later-license-file:        LICENSE-author:              Jack Kelly-maintainer:          jack@jackkelly.name-copyright:           (c) 2019 Jack Kelly-category:            Network-build-type:          Simple-extra-source-files:  CHANGELOG.md, README.md-tested-with:         GHC == 8.6.5+cabal-version:      2.2+name:               reflex-libtelnet+version:            0.2.0.0+synopsis:           Reflex bindings for libtelnet+description:+  Wraps https://git.sr.ht/~jack/libtelnet-haskell , so you+  can handle telnet data streams using reflex+  'Reflex.Event's. See @README.md@ or+  "Reflex.LibTelnet" to get started. +homepage:           https://git.sr.ht/~jack/reflex-libtelnet+bug-reports:        https://todo.sr.ht/~jack/reflex-libtelnet+license:            GPL-3.0-or-later+license-file:       LICENSE+author:             Jack Kelly+maintainer:         jack@jackkelly.name+copyright:          (c) 2019, 2022 Jack Kelly+category:           Network+build-type:         Simple+extra-source-files:+  CHANGELOG.md+  README.md++tested-with:        GHC ==8.8.4 || ==8.10.7 || ==9.0.2+ library-  ghc-options:         -Wall-  exposed-modules:     Reflex.LibTelnet-  build-depends:       base >= 4.11 && < 4.13-                     , bytestring >= 0.10.8.2 && < 0.11-                     , data-default >= 0.7.1.1 && < 0.8-                     , dependent-map >= 0.3 && < 0.4-                     , dependent-sum >= 0.6.2.0 && <= 0.7-                     , dependent-sum-template >= 0.1 && < 0.2-                     , lens >= 4.16.1 && < 4.18-                     , libtelnet >= 0.1 && < 0.2-                     , reflex >= 0.5 && < 0.7-  hs-source-dirs:      src-  default-language:    Haskell2010+  ghc-options:      -Wall+  exposed-modules:  Reflex.LibTelnet+  build-depends:+    , base                    >=4.11     && <4.16+    , bytestring              >=0.10.8.2 && <0.12+    , dependent-map           >=0.3      && <0.5+    , dependent-sum           >=0.6.2.0  && <=0.8+    , dependent-sum-template  ^>=0.1+    , libtelnet               ^>=0.1+    , reflex                  >=0.5      && <0.9++  hs-source-dirs:   src+  default-language: Haskell2010  source-repository head   type:     git
src/Reflex/LibTelnet.hs view
@@ -1,14 +1,16 @@-{-# LANGUAGE FlexibleContexts    #-}-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE LambdaCase          #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell     #-}+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TemplateHaskell       #-}  {-| Module      : Reflex.LibTelnet Description : Reflex wrapper around libtelnet-Copyright   : (c) 2019 Jack Kelly+Copyright   : (c) 2019, 2022 Jack Kelly License     : GPL-3.0-or-later Maintainer  : jack@jackkelly.name Stability   : experimental@@ -16,8 +18,8 @@  How to run a libtelnet state tracker off Reflex 'Event's: -1. Construct a 'TelnetConfig' by using 'def' to get an empty config,-   and then fill it out using record updates or lenses.+1. Construct a 'TelnetConfig' by using 'newTelnetConfig' to get an+   empty config, and then fill it out using record updates or lenses.  2. Start a state tracker by calling 'telnet' on your config. @@ -30,58 +32,21 @@      -- * Input Events   , TelnetConfig(..)+  , newTelnetConfig      -- * Output Events   , TelnetEvents(..)--    -- * Lenses-    -- ** TelnetConfig-  , cOptions-  , cFlags-  , cRecv-  , cSend-  , cIac-  , cNegotiate-  , cSubnegotiation-  , cBeginCompress2-  , cNewEnvironSend-  , cNewEnviron-  , cTTypeSend-  , cTTypeIs-  , cSendZmp-  , cSendMssp--    -- ** TelnetEvents-  , eReceived-  , eSend-  , eWarning-  , eError-  , eIac-  , eWill-  , eWont-  , eDo-  , eDont-  , eSubnegotiation-  , eZmp-  , eTerminalTypeSend-  , eTerminalTypeIs-  , eCompress-  , eEnvironSend-  , eEnviron-  , eMssp-  , eException   ) where  import           Control.Exception (catch)-import           Control.Lens (Lens', (^.), makeLenses) import           Control.Monad.IO.Class (MonadIO(..)) import           Data.ByteString (ByteString)-import           Data.Default (Default(..)) import qualified Data.Dependent.Map as DMap import           Data.Dependent.Sum ((==>)) import           Data.Functor ((<&>)) import           Data.GADT.Compare.TH (deriveGCompare, deriveGEq) import           Data.GADT.Show.TH (deriveGShow)+import           GHC.Generics (Generic) import           Network.Telnet.LibTelnet (Telnet) import qualified Network.Telnet.LibTelnet as Telnet import           Network.Telnet.LibTelnet.Iac (Iac)@@ -94,126 +59,124 @@ -- event" describes all events the state tracker listens to; it -- doesn't care whether the events are coming from "above" or "below" -- itself in the application stack. Data arriving on the socket--- ('_cRecv') is an "input event" from "below"; data pasing through--- libtelnet on its way out to the socket ('_cSend') is also an "input+-- (@recv@) is an "input event" from "below"; data pasing through+-- libtelnet on its way out to the socket (@send@) is also an "input -- event", but from "above". -- -- You will almost certainly want to: ----- 1. use 'def' to get an empty 'TelnetConfig';--- 2. replace the '_cRecv' event with incoming socket data; and--- 3. replace the '_cSend' event with outgoing data from your application.+-- 1. use 'newTelnetConfig' to get an empty 'TelnetConfig';+-- 2. replace the @recv@ event with incoming socket data; and+-- 3. replace the @send@ event with outgoing data from your application. -- -- @since 0.1.0.0 data TelnetConfig t = TelnetConfig-  { _cOptions :: [Telnet.OptionSpec]+  { options :: [Telnet.OptionSpec]     -- ^ Passed to 'Telnet.telnetInit'-  , _cFlags :: [Telnet.Flag]+  , flags :: [Telnet.Flag]     -- ^ Passed to 'Telnet.telnetInit'-  , _cRecv :: Event t ByteString+  , recv :: Event t ByteString     -- ^ 'Telnet.telnetRecv' - "I just received this data, please decode it"-  , _cSend :: Event t ByteString+  , send :: Event t ByteString     -- ^ 'Telnet.telnetSend' - "I want to send this data out, please encode it"-  , _cIac :: Event t Iac+  , iac :: Event t Iac     -- ^ 'Telnet.telnetIac'-  , _cNegotiate :: Event t (Iac, Option)+  , negotiate :: Event t (Iac, Option)     -- ^ 'Telnet.telnetNegotiate'-  , _cSubnegotiation :: Event t (Option, ByteString)+  , subnegotiation :: Event t (Option, ByteString)     -- ^ 'Telnet.telnetSubnegotiation'-  , _cBeginCompress2 :: Event t ()+  , beginCompress2 :: Event t ()     -- ^ 'Telnet.telnetBeginCompress2'-  , _cNewEnvironSend :: Event t [(Telnet.Var, ByteString)]+  , newEnvironSend :: Event t [(Telnet.Var, ByteString)]     -- ^ 'Telnet.telnetNewEnvironSend'-  , _cNewEnviron :: Event t ( Telnet.IsInfo+  , newEnviron :: Event t ( Telnet.IsInfo                             , [(Telnet.Var, ByteString, ByteString)]                             )     -- ^ 'Telnet.telnetNewEnviron'-  , _cTTypeSend :: Event t ()+  , tTypeSend :: Event t ()     -- ^ 'Telnet.telnetTTypeSend'-  , _cTTypeIs :: Event t ByteString+  , tTypeIs :: Event t ByteString     -- ^ 'Telnet.telnetTTypeIs'-  , _cSendZmp :: Event t [ByteString]+  , sendZmp :: Event t [ByteString]     -- ^ 'Telnet.telnetSendZmp'-  , _cSendMssp :: Event t [(ByteString, [ByteString])]+  , sendMssp :: Event t [(ByteString, [ByteString])]     -- ^ 'Telnet.telnetSendMssp'   }--$(makeLenses ''TelnetConfig)+  deriving Generic -- ^ @since 0.2.0.0  -- | No options set and all events are 'never'. ----- @since 0.1.0.0-instance Reflex t => Default (TelnetConfig t) where-  def = TelnetConfig-    { _cOptions = []-    , _cFlags = []-    , _cRecv = never-    , _cSend = never-    , _cIac = never-    , _cNegotiate = never-    , _cSubnegotiation = never-    , _cBeginCompress2 = never-    , _cNewEnvironSend = never-    , _cNewEnviron = never-    , _cTTypeSend = never-    , _cTTypeIs = never-    , _cSendZmp = never-    , _cSendMssp = never-    }+-- @since 0.2.0.0+newTelnetConfig :: Reflex t => TelnetConfig t+newTelnetConfig = TelnetConfig+  { options = []+  , flags = []+  , recv = never+  , send = never+  , iac = never+  , negotiate = never+  , subnegotiation = never+  , beginCompress2 = never+  , newEnvironSend = never+  , newEnviron = never+  , tTypeSend = never+  , tTypeIs = never+  , sendZmp = never+  , sendMssp = never+  }  -- | The libtelnet 'Telnet.Event' type is fanned out into a set of -- individual "output events". An "output event" describes all events -- that should be listened to, regardless of whether they are going--- "up" or "down" the application stack: parsed data ('_eReceived') is+-- "up" or "down" the application stack: parsed data (@received@) is -- an "output event" that should be listened to by the layer "above";--- encoded data that should go to a socket ('_eSend') is also an+-- encoded data that should go to a socket (@send@) is also an -- "output event", but listened to by the layer below. -- -- @since 0.1.0.0 data TelnetEvents t = TelnetEvents-  { _eReceived :: Event t ByteString+  { received :: Event t ByteString     -- ^ 'Telnet.Received' - "Here is some decoded data, please send     -- it up to the application"-  , _eSend :: Event t ByteString+  , send :: Event t ByteString     -- ^ 'Telnet.Send' - "Here is some encoded data, please send it     -- out on the socket"-  , _eWarning :: Event t Telnet.Err+  , warning :: Event t Telnet.Err     -- ^ 'Telnet.Warning'-  , _eError :: Event t Telnet.Err+  , error :: Event t Telnet.Err     -- ^ 'Telnet.Error'-  , _eIac :: Event t Iac+  , iac :: Event t Iac     -- ^ 'Telnet.Iac'-  , _eWill :: Event t Option+  , will :: Event t Option     -- ^ 'Telnet.Will'-  , _eWont :: Event t Option+  , wont :: Event t Option     -- ^ 'Telnet.Wont'-  , _eDo :: Event t Option+  , do_ :: Event t Option     -- ^ 'Telnet.Do'-  , _eDont :: Event t Option+  , dont :: Event t Option     -- ^ 'Telnet.Dont'-  , _eSubnegotiation :: Event t (Option, ByteString)+  , subnegotiation :: Event t (Option, ByteString)     -- ^ 'Telnet.Subnegotiation'-  , _eZmp :: Event t [ByteString]+  , zmp :: Event t [ByteString]     -- ^ 'Telnet.Zmp'-  , _eTerminalTypeSend :: Event t ()+  , terminalTypeSend :: Event t ()     -- ^ 'Telnet.TerminalTypeSend'-  , _eTerminalTypeIs :: Event t ByteString+  , terminalTypeIs :: Event t ByteString     -- ^ 'Telnet.TerminalTypeIs'-  , _eCompress :: Event t Bool+  , compress :: Event t Bool     -- ^ 'Telnet.Compress'-  , _eEnvironSend :: Event t [(Telnet.Var, ByteString)]+  , environSend :: Event t [(Telnet.Var, ByteString)]     -- ^ 'Telnet.EnvironSend'-  , _eEnviron :: Event t (Telnet.IsInfo, [(Telnet.Var, ByteString, ByteString)])+  , environ :: Event t (Telnet.IsInfo, [(Telnet.Var, ByteString, ByteString)])     -- ^ 'Telnet.Environ'-  , _eMssp :: Event t [(ByteString, [ByteString])]+  , mssp :: Event t [(ByteString, [ByteString])]     -- ^ 'Telnet.Mssp'-  , _eException :: Event t Telnet.TelnetException+  , exception :: Event t Telnet.TelnetException     -- ^ Exceptions thrown by the binding are caught and emitted     -- here. Protocol errors and warnings are emitted on the-    -- '_eWarning' and '_eError' events.+    -- @warning@ and @error@ events.   }--$(makeLenses ''TelnetEvents)+  deriving Generic -- ^ @since 0.2.0.0  data EventKey a where   Received :: EventKey ByteString@@ -239,9 +202,9 @@ $(deriveGShow ''EventKey)  -- | Process telnet streams using--- <https://github.com/seanmiddleditch/libtelnet libtelnet>. The--- easiest way to get a @'TelnetConfig' t@ is through its 'Default'--- instance; see 'TelnetConfig' for details.+-- <https://github.com/seanmiddleditch/libtelnet libtelnet>. Use+-- 'newTelnetConfig' to get a @'TelnetConfig' t@, and update it as+-- required. -- -- @since 0.1.0.0 telnet@@ -256,31 +219,31 @@   (telnetE, fireTelnetE) <- newTriggerEvent   (telnetExcE, fireTelnetExcE) <- newTriggerEvent   t <- liftIO $ Telnet.telnetInit-    (config ^. cOptions)-    (config ^. cFlags)+    (options config)+    (flags config)     (const fireTelnetE)    let     perform       :: (Telnet -> a -> IO ())-      -> Lens' (TelnetConfig t) (Event t a)+      -> (TelnetConfig t -> Event t a)       -> m ()-    perform f l = performEvent_ $ action <$> config ^. l where-      action x = liftIO $ f t x `catch` \(ex :: Telnet.TelnetException) ->+    perform act get = performEvent_ $ action <$> get config where+      action x = liftIO $ act t x `catch` \(ex :: Telnet.TelnetException) ->         fireTelnetExcE ex -  perform Telnet.telnetRecv cRecv-  perform Telnet.telnetSend cSend-  perform Telnet.telnetIac cIac-  perform (uncurry . Telnet.telnetNegotiate) cNegotiate-  perform (uncurry . Telnet.telnetSubnegotiation) cSubnegotiation-  perform (const . Telnet.telnetBeginCompress2) cBeginCompress2-  perform Telnet.telnetNewEnvironSend cNewEnvironSend-  perform (uncurry . Telnet.telnetNewEnviron) cNewEnviron-  perform (const . Telnet.telnetTTypeSend) cTTypeSend-  perform Telnet.telnetTTypeIs cTTypeIs-  perform Telnet.telnetSendZmp cSendZmp-  perform Telnet.telnetSendMssp cSendMssp+  perform Telnet.telnetRecv recv+  perform Telnet.telnetSend send+  perform Telnet.telnetIac iac+  perform (uncurry . Telnet.telnetNegotiate) negotiate+  perform (uncurry . Telnet.telnetSubnegotiation) subnegotiation+  perform (const . Telnet.telnetBeginCompress2) beginCompress2+  perform Telnet.telnetNewEnvironSend newEnvironSend+  perform (uncurry . Telnet.telnetNewEnviron) newEnviron+  perform (const . Telnet.telnetTTypeSend) tTypeSend+  perform Telnet.telnetTTypeIs tTypeIs+  perform Telnet.telnetSendZmp sendZmp+  perform Telnet.telnetSendMssp sendMssp    let     selector :: EventSelector t EventKey@@ -304,22 +267,22 @@       Telnet.Mssp msg -> Mssp ==> msg    pure $ TelnetEvents-    { _eReceived = selector `select` Received-    , _eSend = selector `select` Send-    , _eWarning = selector `select` Warning-    , _eError = selector `select` Error-    , _eIac = selector `select` Iac-    , _eWill  = selector `select` Will-    , _eWont  = selector `select` Wont-    , _eDo  = selector `select` Do-    , _eDont  = selector `select` Dont-    , _eSubnegotiation  = selector `select` Subnegotiation-    , _eZmp  = selector `select` Zmp-    , _eTerminalTypeSend  = selector `select` TerminalTypeSend-    , _eTerminalTypeIs  = selector `select` TerminalTypeIs-    , _eCompress  = selector `select` Compress-    , _eEnvironSend  = selector `select` EnvironSend-    , _eEnviron  = selector `select` Environ-    , _eMssp  = selector `select` Mssp-    , _eException = telnetExcE+    { received = selector `select` Received+    , send = selector `select` Send+    , warning = selector `select` Warning+    , error = selector `select` Error+    , iac = selector `select` Iac+    , will = selector `select` Will+    , wont = selector `select` Wont+    , do_ = selector `select` Do+    , dont = selector `select` Dont+    , subnegotiation  = selector `select` Subnegotiation+    , zmp = selector `select` Zmp+    , terminalTypeSend = selector `select` TerminalTypeSend+    , terminalTypeIs = selector `select` TerminalTypeIs+    , compress = selector `select` Compress+    , environSend = selector `select` EnvironSend+    , environ = selector `select` Environ+    , mssp = selector `select` Mssp+    , exception = telnetExcE     }