packages feed

sport 2.0.0.0 → 3.0.0.0

raw patch · 7 files changed

+73/−145 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Sport: getOpenSportCfg :: Sport -> STM (Maybe SportCfg)
- Sport: runSport :: Sport -> IO a
- Sport: withOpenSport :: SportCfg -> (Sport -> IO a) -> IO a
- Sport.Sport: getOpenSportCfg :: Sport -> STM (Maybe SportCfg)
- Sport.Sport: instance GHC.Classes.Eq Sport.Sport.State
- Sport.Sport: runSport :: Sport -> IO a
- Sport.Sport: withOpenSport :: SportCfg -> (Sport -> IO a) -> IO a
+ Sport: withNewSport :: SportCfg -> (Sport -> IO a) -> IO a
+ Sport.Sport: withNewSport :: SportCfg -> (Sport -> IO a) -> IO a
- Sport: withSport :: (Sport -> IO a) -> IO a
+ Sport: withSport :: Sport -> SportCfg -> IO a -> IO a
- Sport.Sport: withSport :: (Sport -> IO a) -> IO a
+ Sport.Sport: withSport :: Sport -> SportCfg -> IO a -> IO a

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for sport +## 3.0.0.0 -- 6-7-2026++* Update `withSport`, add `withNewSport`+* Remove `runSport` daemon+ ## 2.0.0.0 -- 6-6-2026  * Add `flushSport`, `withOpenSport`
README.md view
@@ -5,7 +5,8 @@ ```hs import Sport -main = withSport $ \s -> do+main = do+  s <- newSportIO   openSport s defSportCfg{path="/dev/ttyUSB1"}   bs <- readSport s 64   writeSport s bs
lib/Sport.hs view
@@ -3,24 +3,23 @@ -- @ -- import Sport ----- main = withSport $ \\s -> do+-- main = do+--   s <- newSportIO --   openSport s defSportCfg{path="\/dev\/ttyUSB1", speed=B19200} --   bs <- readSport s 64 --   writeSport s bs -- @ module Sport-  ( -- *** Handle & Daemon+  ( -- *** Handle     Sport-  , withSport-  , withOpenSport+  , withNewSport   , newSportIO   , newSport-  , runSport   , -- *** Open & Close-    openSport+    withSport+  , openSport   , isOpenSport   , getSportCfg-  , getOpenSportCfg   , defSportCfg   , SportCfg(..)   , BufferMode(..)
lib/Sport/Main.hs view
@@ -7,14 +7,15 @@ import System.IO import System.Posix +-- | Main executable sportMain :: IO ()-sportMain =-  withSport $ \s -> do-    putStrLn "...UNIX.SERIAL.PORT..."-    hSetBuffering stdin  NoBuffering-    hSetBuffering stdout NoBuffering-    openSport s defSportCfg{speed = B19200}-    concurrently_ (reading s) (writing s)+sportMain = do+  s <- newSportIO+  putStrLn "...UNIX.SERIAL.PORT..."+  hSetBuffering stdin  NoBuffering+  hSetBuffering stdout NoBuffering+  openSport s defSportCfg{speed = B19200}+  concurrently_ (reading s) (writing s)  reading :: Sport -> IO a reading s = forever $ putChar . BS.head =<< readSport s 1
lib/Sport/Sport.hs view
@@ -1,15 +1,13 @@ -- | Internal high-level serial port module Sport.Sport   ( Sport+  , withNewSport   , newSportIO   , newSport   , withSport-  , withOpenSport-  , runSport   , openSport   , isOpenSport   , getSportCfg-  , getOpenSportCfg   , defSportCfg   , SportCfg(..)   , closeSport@@ -21,21 +19,21 @@   , displaySportException   ) where -import Control.Concurrent.Async import Control.Concurrent.STM import Control.Exception import Control.Monad import qualified Data.ByteString as Strict import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as BS-import Data.Functor-import Data.Maybe import qualified Sport.Serial as S import System.IO import System.Posix  -- | Serial port handle-newtype Sport = Sport (TVar State)+data Sport = Sport+  { cfgVar  :: TMVar SportCfg+  , hndlVar :: TMVar Handle+  }  -- | Acquire IO serial port newSportIO :: IO Sport@@ -43,28 +41,11 @@  -- | Acquire STM serial port newSport :: STM Sport-newSport = Sport <$> newTVar Closed--data State-  = Closed-  | Opening SportCfg (TMVar (Either SomeException ()))-  | Open SportCfg Handle-  deriving Eq--readHandle :: Sport -> STM Handle-readHandle (Sport s) = do-  st <- readTVar s-  case st of-    Closed    -> retry-    Opening{} -> retry-    Open _ h  -> return h--tryReadHandle :: Sport -> STM (Maybe Handle)-tryReadHandle s = Just `fmap` readHandle s `orElse` pure Nothing+newSport = Sport <$> newEmptyTMVar <*> newEmptyTMVar  -- | Check if serial port is open isOpenSport :: Sport -> STM Bool-isOpenSport s = isJust <$> tryReadHandle s+isOpenSport = fmap not . isEmptyTMVar . cfgVar  -- | Handle and serial configuration data SportCfg = SportCfg@@ -94,63 +75,50 @@     (S.rtimeout S.defSerialCfg)     (S.excl S.defSerialCfg) +-- | Acquire new handle 'newSportIO' then bracket+-- 'openSport' with a configuration to 'closeSport'.+withNewSport :: SportCfg -> (Sport -> IO a) -> IO a+withNewSport cfg k = do+  s <- newSportIO+  withSport s cfg $ k s++-- | Bracket 'openSport' and 'closeSport'.+withSport :: Sport -> SportCfg -> IO a -> IO a+withSport s cfg = bracket_ (openSport s cfg) (closeSport s)+ -- | Open the serial port. Throw 'SportAlreadyOpen' if the -- serial port was already opened. openSport :: Sport -> SportCfg -> IO ()-openSport s@(Sport state) cfg' = do-  res <- newEmptyTMVarIO-  atomically $ do-    cfgM <- getSportCfg s-    case cfgM of-      Nothing  -> writeTVar state $ Opening cfg' res-      Just cfg -> throwSTM $ SportAlreadyOpen $ path cfg-  atomically $ do-    result <- takeTMVar res-    case result of-      Left err -> throwSTM err-      Right () -> return ()+openSport s cfg' =+  bracketOnError (S.openSerial $ toSerialCfg cfg') hClose $ \serial -> do+    hSetBinaryMode serial $ binaryMode cfg'+    hSetBuffering  serial $ bufferMode cfg'+    atomically $ do+      cfgM <- getSportCfg s+      case cfgM of+        Just cfg -> throwSTM $ SportAlreadyOpen $ path cfg+        Nothing  -> do+          putTMVar (cfgVar  s) cfg'+          putTMVar (hndlVar s) serial  -- | Get current configuration. Returns 'Just' if--- the current state is opening or open. See also 'getOpenSportCfg'.+-- the current state is open. getSportCfg :: Sport -> STM (Maybe SportCfg)-getSportCfg (Sport s) = do-  st <- readTVar s-  return $ case st of-    Closed        -> Nothing-    Opening cfg _ -> Just cfg-    Open    cfg _ -> Just cfg---- | Get current open configuration. Returns 'Just' if--- the current state is open. See also 'getSportCfg'.-getOpenSportCfg :: Sport -> STM (Maybe SportCfg)-getOpenSportCfg (Sport s) = do-  st <- readTVar s-  return $ case st of-    Closed     -> Nothing-    Opening{}  -> Nothing-    Open cfg _ -> Just cfg+getSportCfg = tryReadTMVar . cfgVar  -- | Close the serial port. closeSport :: Sport -> IO ()-closeSport (Sport s) = join $ atomically $ do-  st <- readTVar s-  writeTVar s Closed-  closeState st--closeState :: State -> STM (IO ())-closeState Closed          = return $ return ()-closeState (Opening _ res) = closeResponse res $> return ()-closeState (Open _ serial) = return $ hClose serial--closeResponse :: TMVar (Either SomeException a) -> STM ()-closeResponse res = void $ tryPutTMVar res $ Left $ toException SportClosed+closeSport s = join $ atomically $ do+  _  <- tryTakeTMVar $ cfgVar  s+  hM <- tryTakeTMVar $ hndlVar s+  return $ mapM_ hClose hM  -- | Read n bytes from the serial port. If the serial port is -- closed then throw 'SportClosed'. Blocks until all n bytes -- are available. readSport :: Sport -> Int -> IO ByteString readSport s n = do-  h <- atomically $ readHandle s `orElse` throwSTM SportClosed+  h <- atomically $ readTMVar (hndlVar s) `orElse` throwSTM SportClosed   BS.hGet h n  -- | Read up to n bytes from the serial port. If the serial port@@ -158,7 +126,7 @@ -- the n bytes are available. readSomeSport :: Sport -> Int -> IO ByteString readSomeSport s n = do-  h <- atomically $ readHandle s `orElse` throwSTM SportClosed+  h <- atomically $ readTMVar (hndlVar s) `orElse` throwSTM SportClosed   BS.fromStrict <$> Strict.hGetSome h n  -- | Write bytes to the serial port. If the serial port@@ -166,54 +134,15 @@ -- port is busy handling a concurrent request. writeSport :: Sport -> ByteString -> IO () writeSport s bs = do-  h <- atomically $ readHandle s `orElse` throwSTM SportClosed+  h <- atomically $ readTMVar (hndlVar s) `orElse` throwSTM SportClosed   BS.hPut h bs  -- | Flush the serial handle. Do nothing if closed. flushSport :: Sport -> IO () flushSport s = do-  hM <- atomically $ tryReadHandle s+  hM <- atomically $ tryReadTMVar $ hndlVar s   mapM_ hFlush hM --- | Acquire a serial port and run the daemon.-withSport :: (Sport -> IO a) -> IO a-withSport k = do-  s <- newSportIO-  either id id <$> race (k s) (runSport s)---- | Construct the handle, run the daemon,--- and open the configured serial port. The serial port--- is closed on leaving scope.-withOpenSport :: SportCfg -> (Sport -> IO a) -> IO a-withOpenSport cfg k =-  withSport $ \s -> bracket_ (openSport s cfg) (closeSport s) (k s)---- | Run the serial port daemon and process requests.-runSport :: Sport -> IO a-runSport s@(Sport state) =-  forever (runState s =<< readTVarIO state) `onException` closeSport s--runState :: Sport -> State -> IO ()-runState s@(Sport state) st = case st of-  Closed -> waitNewState s st-  Opening cfg res ->-    handleException res $-      opening s cfg res-        `onException` atomically (writeTVar state Closed)-  Open{} -> waitNewState s st--waitNewState :: Sport -> State -> IO ()-waitNewState (Sport s) st = atomically $ check . (st /=) =<< readTVar s--opening :: Sport -> SportCfg -> TMVar (Either SomeException ()) -> IO ()-opening (Sport s) cfg res =-  bracketOnError (S.openSerial $ toSerialCfg cfg) hClose $ \serial -> do-    hSetBinaryMode serial $ binaryMode cfg-    hSetBuffering serial $ bufferMode cfg-    atomically $ do-      writeTVar s $ Open cfg serial-      writeTMVar res $ Right ()- toSerialCfg :: SportCfg -> S.SerialCfg toSerialCfg s =   S.SerialCfg@@ -224,12 +153,6 @@     (stopBits s)     (rtimeout s)     (excl s)--handleException :: TMVar (Either SomeException a) -> IO () -> IO ()-handleException res k = k `catches`-  [ Handler $ \e -> throwIO (e :: SomeAsyncException)-  , Handler $ \e -> atomically $ void $ tryPutTMVar res $ Left (e :: SomeException)-  ]  -- | Serial port exceptions data SportException
sport.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               sport-version:            2.0.0.0+version:            3.0.0.0 synopsis:           UNIX serial port description:        Concurrent serial IO license:            MIT
test/Main.hs view
@@ -10,16 +10,15 @@   hSetBuffering stdout NoBuffering   putStrLn "SPORT TEST"   s <- newSportIO-  race_ (runSport s) $ do-    putStr "OPENING..."-    openSport s defSportCfg{speed=B19200}-    putStrLn "DONE"-    let bs = BS.replicate 256 0xDC-    putStr "XFERING..."-    bs' <- fst <$> concurrently (readSport s 10) (writeSport s bs)-    putStrLn "DONE"-    putStr "COMPARING..."-    putStrLn $ if BS.take 10 bs == bs' then "PASS" else "FAIL"-    putStr "CLOSING..."-    closeSport s-    putStrLn "DONE"+  putStr "OPENING..."+  openSport s defSportCfg{speed=B19200}+  putStrLn "DONE"+  let bs = BS.replicate 256 0xDC+  putStr "XFERING..."+  bs' <- fst <$> concurrently (readSport s 10) (writeSport s bs)+  putStrLn "DONE"+  putStr "COMPARING..."+  putStrLn $ if BS.take 10 bs == bs' then "PASS" else "FAIL"+  putStr "CLOSING..."+  closeSport s+  putStrLn "DONE"