sport 0.2.0.0 → 1.0.0.0
raw patch · 6 files changed
+86/−26 lines, 6 filesdep +sportdep ~basenew-component:exe:sportPVP ok
version bump matches the API change (PVP)
Dependencies added: sport
Dependency ranges changed: base
API changes (from Hackage documentation)
- Sport.Sport: instance GHC.Internal.Read.Read Sport.Sport.SportException
+ Sport: isOpenSport :: Sport -> STM Bool
+ Sport.Sport: instance GHC.Internal.Show.Show Sport.Sport.SportCfg
+ Sport.Sport: isOpenSport :: Sport -> STM Bool
- Sport: SportAlreadyOpen :: SportException
+ Sport: SportAlreadyOpen :: String -> SportException
- Sport.Sport: SportAlreadyOpen :: SportException
+ Sport.Sport: SportAlreadyOpen :: String -> SportException
Files
- CHANGELOG.md +6/−0
- exe/Main.hs +6/−0
- lib/Sport.hs +1/−0
- lib/Sport/Sport.hs +27/−9
- sport.cabal +20/−17
- test/Main.hs +26/−0
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for sport +## 1.0.0.0 -- 4-30-2026++* Fix `SportAlreadyOpen` bug+* Update exception types+* Add `isOpenSport` to STM API+ ## 0.2.0.0 -- 4-21-2026 * Expose current configuration in STM
+ exe/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Sport.Main++main :: IO ()+main = sportMain
lib/Sport.hs view
@@ -17,6 +17,7 @@ , runSport , -- *** Open & Close openSport+ , isOpenSport , getSportCfg , defSportCfg , SportCfg(..)
lib/Sport/Sport.hs view
@@ -5,6 +5,7 @@ , withSport , runSport , openSport+ , isOpenSport , getSportCfg , defSportCfg , SportCfg(..)@@ -52,6 +53,14 @@ | Open SportCfg Handle deriving Eq +isOpen :: State -> Bool+isOpen Open{} = True+isOpen _ = False++-- | Check if serial port is open+isOpenSport :: Sport -> STM Bool+isOpenSport = fmap isOpen . readTVar . state+ data RdCmd = Rd Int (TMVar (Either SomeException ByteString)) | RdSome Int (TMVar (Either SomeException ByteString))@@ -71,7 +80,7 @@ , rtimeout :: Maybe Int , excl :: Bool -- ^ exclusive }- deriving Eq+ deriving (Eq, Show) -- | Binary with no buffering defSportCfg :: SportCfg@@ -90,13 +99,14 @@ -- | Open the serial port. Throw 'SportAlreadyOpen' if the -- serial port was already opened. openSport :: Sport -> SportCfg -> IO ()-openSport (Sport s _ _) cfg = do+openSport (Sport s _ _) cfg' = do res <- newEmptyTMVarIO atomically $ do st <- readTVar s case st of- Closed -> writeTVar s $ Opening cfg res- _ -> throwSTM SportAlreadyOpen+ Closed -> writeTVar s $ Opening cfg' res+ Opening cfg _ -> throwSTM $ SportAlreadyOpen $ path cfg+ Open cfg _ -> throwSTM $ SportAlreadyOpen $ path cfg atomically $ do result <- takeTMVar res case result of@@ -198,9 +208,12 @@ runState :: Sport -> State -> IO () runState s st = case st of- Closed -> waitNewState s st- Opening cfg res -> handleException res $ opening s cfg res- Open _ serial -> runConcurrently $ asum $ map Concurrently+ Closed -> waitNewState s st+ Opening cfg res ->+ handleException res $+ opening s cfg res+ `onException` atomically (writeTVar (state s) Closed)+ Open _ serial -> runConcurrently $ asum $ map Concurrently [ waitNewState s st , readHandler s serial , writeHandler s serial@@ -259,7 +272,12 @@ -- | User required an open sport but it was closed. = SportClosed -- | User attempted to open a serial port that was already open.- | SportAlreadyOpen- deriving (Eq, Read, Show)+ | SportAlreadyOpen String+ deriving Eq++instance Show SportException where+ show e = "sport exception: " <> case e of+ SportClosed -> "serial port closed"+ SportAlreadyOpen p -> "serial port already open: " <> p instance Exception SportException
sport.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: sport-version: 0.2.0.0+version: 1.0.0.0 synopsis: UNIX serial port description: Concurrent serial IO license: MIT@@ -18,7 +18,7 @@ location: https://github.com/standardsemiconductor/sport library- ghc-options: -Wall -O2+ ghc-options: -Wall -Wunused-packages -O2 default-language: Haskell2010 hs-source-dirs: lib exposed-modules: Sport,@@ -31,19 +31,22 @@ stm, unix ---executable sport--- ghc-options: -Wall -O2 -threaded--- default-language: Haskell2010--- hs-source-dirs: exe--- main-is: Main.hs--- build-depends: base,--- sport+executable sport+ ghc-options: -Wall -O2 -threaded+ default-language: Haskell2010+ hs-source-dirs: exe+ main-is: Main.hs+ build-depends: base,+ sport ---test-suite test--- ghc-options: -Wall--- default-language: Haskell2010--- hs-source-dirs: test--- type: exitcode-stdio-1.0--- main-is: Main.hs--- build-depends: base,--- sport+test-suite test+ ghc-options: -Wall -Wunused-packages -O2 -threaded+ default-language: Haskell2010+ hs-source-dirs: test+ type: exitcode-stdio-1.0+ main-is: Main.hs+ build-depends: async,+ base,+ bytestring,+ sport,+ stm
+ test/Main.hs view
@@ -0,0 +1,26 @@+module Main (main) where++import Control.Concurrent.Async+import Control.Concurrent.STM+import qualified Data.ByteString.Lazy as BS+import Sport+import System.IO++main :: IO ()+main = do+ 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"