1
0
Fork 0

Can’t have a status-script without GADTs can we?

This commit is contained in:
Malte 2023-03-02 04:58:22 +01:00
parent 88cc11fb88
commit a09071f8f6

View file

@ -1,5 +1,6 @@
{-# LANGUAGE BlockArguments #-} {-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
@ -19,6 +20,7 @@ import Data.ByteString.Char8 qualified as ByteString
import Data.ByteString.Lazy qualified as LBS import Data.ByteString.Lazy qualified as LBS
import Data.String.Interpolate (i) import Data.String.Interpolate (i)
import Data.Text qualified as Text import Data.Text qualified as Text
import Data.Unique qualified as Unique
import Relude import Relude
import Say (sayErr) import Say (sayErr)
import Shh (ExecReference (Absolute), Proc, captureTrim, exe, ignoreFailure, load, readInputLines, (|>)) import Shh (ExecReference (Absolute), Proc, captureTrim, exe, ignoreFailure, load, readInputLines, (|>))
@ -47,35 +49,54 @@ isUnpushed gitDir = do
tryCmd :: Proc a -> IO LBS.ByteString tryCmd :: Proc a -> IO LBS.ByteString
tryCmd x = ignoreFailure x |> captureTrim tryCmd x = ignoreFailure x |> captureTrim
data Var a = MkVar newtype Var a = MkVar
{ value :: TVar a { value :: TVar (a, Unique.Unique)
, update :: TMVar ()
} }
getUnique = fmap snd . readTVar . value
getVal = fmap fst . readTVar . value
newVar :: a -> IO (Var a) newVar :: a -> IO (Var a)
newVar initial_value = atomically $ MkVar <$> newTVar initial_value <*> newEmptyTMVar newVar initial_value = do
unique <- Unique.newUnique
MkVar <$> newTVarIO (initial_value, unique)
newMaybeVar :: IO (Var (Maybe a)) newMaybeVar :: IO (Var (Maybe a))
newMaybeVar = atomically $ MkVar <$> newTVar Nothing <*> newEmptyTMVar newMaybeVar = newVar Nothing
type Vars = [Var (Maybe Text)] type Vars = [Var (Maybe Text)]
type Module a = Var a -> IO Void type Module a = Var a -> IO Void
repeatM :: IO a -> IO Void
repeatM action = fix (action >>)
writeVars :: Vars -> IO Void writeVars :: Vars -> IO Void
writeVars vars = repeatM do writeVars vars = onUpdatesMono vars do
outputs <- atomically $ do outputs <- atomically $ mapM getVal vars
updates <- forM vars $ \MkVar{update} -> tryTakeTMVar update
STM.check $ any isJust updates
forM vars \MkVar{value} -> readTVar value
writeFileText "/run/user/1000/status-bar" $ writeFileText "/run/user/1000/status-bar" $
Text.replace "&" "&amp;" $ Text.replace "&" "&amp;" $
Text.unwords $ Text.unwords $
("<executor.markup.true>" :) $ ("<executor.markup.true>" :) $
catMaybes outputs catMaybes outputs
data AnyVar where
AVar :: Var a -> AnyVar
onUpdate :: Var a -> (a -> IO ()) -> IO Void
onUpdate var action = onUpdatesMono [var] do
action =<< atomically (getVal var)
onUpdatesMono :: [Var a] -> IO () -> IO Void
onUpdatesMono = onUpdates . fmap AVar
onUpdates :: [AnyVar] -> IO () -> IO Void
onUpdates vars action = go []
where
go previous_uniques = do
next_uniques <- atomically $ do
current_uniques <- mapM (\(AVar a) -> getUnique a) vars
STM.check $ previous_uniques /= current_uniques
pure current_uniques
action
go next_uniques
runModules :: [Module (Maybe Text)] -> IO () runModules :: [Module (Maybe Text)] -> IO ()
runModules modules = do runModules modules = do
(vars, actions) <- (vars, actions) <-
@ -90,16 +111,17 @@ foldConcurrently_ = Async.mapConcurrently_ id
oneSecond :: Int oneSecond :: Int
oneSecond = 1000000 oneSecond = 1000000
updateVarIfChanged :: Eq a => Var a -> a -> STM () updateVarIfChanged :: Eq a => Var a -> a -> IO ()
updateVarIfChanged MkVar{value, update} new_value = do updateVarIfChanged var@MkVar{value} new_value = do
old_value <- readTVar value new_unique <- Unique.newUnique
unless (old_value == new_value) do atomically do
writeTVar value new_value old_value <- getVal var
putTMVar update () unless (old_value == new_value) do
writeTVar value (new_value, new_unique)
simpleModule :: Eq a => Int -> IO a -> Module a simpleModule :: Eq a => Int -> IO a -> Module a
simpleModule delay action var = repeatM do simpleModule delay action var = forever do
atomically . updateVarIfChanged var =<< action updateVarIfChanged var =<< action
Concurrent.threadDelay delay Concurrent.threadDelay delay
withColor :: Monad m => Text -> Text -> m (Maybe Text) withColor :: Monad m => Text -> Text -> m (Maybe Text)
@ -115,8 +137,7 @@ playerModule :: Module (Maybe Text)
playerModule = \var -> playerModule = \var ->
let update_lines = let update_lines =
mapM_ mapM_
( atomically ( updateVarIfChanged var
. updateVarIfChanged var
. Just . Just
. Text.replace "@Stopped" "" . Text.replace "@Stopped" ""
. Text.replace "@Playing" "" . Text.replace "@Playing" ""
@ -131,7 +152,7 @@ playerModule = \var ->
main :: IO () main :: IO ()
main = do main = do
mode_var <- newVar Unrestricted mode_var <- newVar Unrestricted
let read_mode = readTVarIO (value mode_var) let read_mode = fst <$> readTVarIO (value mode_var)
modules = modules =
[ simpleModule (5 * oneSecond) $ do [ simpleModule (5 * oneSecond) $ do
appointments <- lines . decodeUtf8 <$> tryCmd (khal ["list", "-a", "Standard", "-a", "Planung", "-a", "Uni", "-a", "Maltaire", "now", "2h", "-df", ""]) appointments <- lines . decodeUtf8 <$> tryCmd (khal ["list", "-a", "Standard", "-a", "Planung", "-a", "Uni", "-a", "Maltaire", "now", "2h", "-df", ""])
@ -196,9 +217,8 @@ main = do
when' (system_dirty || modes_dirty) $ withColor "ffff00" [i|Current #{case (system_dirty,modes_dirty) of (True, True) -> "home and system"; (True, _) -> "system"; _ -> "home"} stale|] when' (system_dirty || modes_dirty) $ withColor "ffff00" [i|Current #{case (system_dirty,modes_dirty) of (True, True) -> "home and system"; (True, _) -> "system"; _ -> "home"} stale|]
) )
var var
, \var -> forever do , \var ->
atomically . updateVarIfChanged var =<< withColor "7287fd" . show =<< read_mode onUpdate mode_var $ updateVarIfChanged var . runIdentity . withColor "7287fd" . show
atomically $ takeTMVar (update mode_var)
] ]
foldConcurrently_ foldConcurrently_
[ void $ simpleModule oneSecond getMode mode_var [ void $ simpleModule oneSecond getMode mode_var