ENH use cache (kinda) for dependency tree testing
This commit is contained in:
parent
3644efe205
commit
7cfb799698
|
@ -33,6 +33,7 @@ import System.Posix.Signals
|
||||||
import XMonad.Core
|
import XMonad.Core
|
||||||
( cfgDir
|
( cfgDir
|
||||||
, getDirectories
|
, getDirectories
|
||||||
|
, io
|
||||||
)
|
)
|
||||||
import XMonad.Hooks.DynamicLog (wrap)
|
import XMonad.Hooks.DynamicLog (wrap)
|
||||||
import XMonad.Internal.Command.Power (hasBattery)
|
import XMonad.Internal.Command.Power (hasBattery)
|
||||||
|
@ -55,7 +56,7 @@ import Xmobar.Plugins.Common
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
db <- connectDBus
|
db <- connectDBus
|
||||||
c <- evalConfig db
|
c <- withCache $ evalConfig db
|
||||||
disconnectDBus db
|
disconnectDBus db
|
||||||
-- this is needed to prevent waitForProcess error when forking in plugins (eg
|
-- this is needed to prevent waitForProcess error when forking in plugins (eg
|
||||||
-- alsacmd)
|
-- alsacmd)
|
||||||
|
@ -64,12 +65,12 @@ main = do
|
||||||
hFlush stdout
|
hFlush stdout
|
||||||
xmobar c
|
xmobar c
|
||||||
|
|
||||||
evalConfig :: DBusState -> IO Config
|
evalConfig :: DBusState -> FIO Config
|
||||||
evalConfig db = do
|
evalConfig db = do
|
||||||
cs <- getAllCommands =<< rightPlugins db
|
cs <- getAllCommands <$> rightPlugins db
|
||||||
bf <- getTextFont
|
bf <- getTextFont
|
||||||
(ifs, ios) <- getIconFonts
|
(ifs, ios) <- getIconFonts
|
||||||
d <- cfgDir <$> getDirectories
|
d <- io $ cfgDir <$> getDirectories
|
||||||
return $ config bf ifs ios cs d
|
return $ config bf ifs ios cs d
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -151,21 +152,18 @@ config bf ifs ios br confDir = defaultConfig
|
||||||
-- some commands depend on the presence of interfaces that can only be
|
-- some commands depend on the presence of interfaces that can only be
|
||||||
-- determined at runtime; define these checks here
|
-- determined at runtime; define these checks here
|
||||||
|
|
||||||
getAllCommands :: [Maybe CmdSpec] -> IO BarRegions
|
getAllCommands :: [Maybe CmdSpec] -> BarRegions
|
||||||
getAllCommands right = do
|
getAllCommands right = BarRegions
|
||||||
let left =
|
{ brLeft = [ CmdSpec
|
||||||
[ CmdSpec
|
|
||||||
{ csAlias = "UnsafeStdinReader"
|
{ csAlias = "UnsafeStdinReader"
|
||||||
, csRunnable = Run UnsafeStdinReader
|
, csRunnable = Run UnsafeStdinReader
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
return $ BarRegions
|
|
||||||
{ brLeft = left
|
|
||||||
, brCenter = []
|
, brCenter = []
|
||||||
, brRight = catMaybes right
|
, brRight = catMaybes right
|
||||||
}
|
}
|
||||||
|
|
||||||
rightPlugins :: DBusState -> IO [Maybe CmdSpec]
|
rightPlugins :: DBusState -> FIO [Maybe CmdSpec]
|
||||||
rightPlugins DBusState { dbSesClient = ses, dbSysClient = sys }
|
rightPlugins DBusState { dbSesClient = ses, dbSysClient = sys }
|
||||||
= mapM evalFeature
|
= mapM evalFeature
|
||||||
[ Left getWireless
|
[ Left getWireless
|
||||||
|
@ -201,13 +199,13 @@ getBattery :: BarFeature
|
||||||
getBattery = iconIO_ "battery level indicator" root tree
|
getBattery = iconIO_ "battery level indicator" root tree
|
||||||
where
|
where
|
||||||
root useIcon = IORoot_ (batteryCmd useIcon)
|
root useIcon = IORoot_ (batteryCmd useIcon)
|
||||||
tree = Only_ $ sysTest "Test if battery is present" hasBattery
|
tree = Only_ $ IOTest_ "Test if battery is present" hasBattery
|
||||||
|
|
||||||
getVPN :: Maybe Client -> BarFeature
|
getVPN :: Maybe Client -> BarFeature
|
||||||
getVPN cl = iconDBus_ "VPN status indicator" root $ toAnd vpnDep test
|
getVPN cl = iconDBus_ "VPN status indicator" root $ toAnd vpnDep test
|
||||||
where
|
where
|
||||||
root useIcon tree = DBusRoot_ (const $ vpnCmd useIcon) tree cl
|
root useIcon tree = DBusRoot_ (const $ vpnCmd useIcon) tree cl
|
||||||
test = DBusIO $ sysTest "Use nmcli to test if VPN is present" vpnPresent
|
test = DBusIO $ IOTest_ "Use nmcli to test if VPN is present" vpnPresent
|
||||||
|
|
||||||
getBt :: Maybe Client -> BarFeature
|
getBt :: Maybe Client -> BarFeature
|
||||||
getBt = xmobarDBus "bluetooth status indicator" btDep btCmd
|
getBt = xmobarDBus "bluetooth status indicator" btDep btCmd
|
||||||
|
@ -451,7 +449,7 @@ vpnPresent =
|
||||||
-- ASSUME there is only one text font for this entire configuration. This
|
-- ASSUME there is only one text font for this entire configuration. This
|
||||||
-- will correspond to the first font/offset parameters in the config record.
|
-- will correspond to the first font/offset parameters in the config record.
|
||||||
|
|
||||||
getTextFont :: IO String
|
getTextFont :: FIO String
|
||||||
getTextFont = do
|
getTextFont = do
|
||||||
fb <- evalAlways textFont
|
fb <- evalAlways textFont
|
||||||
return $ fb textFontData
|
return $ fb textFontData
|
||||||
|
@ -459,7 +457,7 @@ getTextFont = do
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | icon fonts
|
-- | icon fonts
|
||||||
|
|
||||||
getIconFonts :: IO ([String], [Int])
|
getIconFonts :: FIO ([String], [Int])
|
||||||
getIconFonts = do
|
getIconFonts = do
|
||||||
fb <- evalSometimes iconFont
|
fb <- evalSometimes iconFont
|
||||||
return $ maybe ([], []) apply fb
|
return $ maybe ([], []) apply fb
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
|
import Control.Concurrent.Lifted (fork)
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
@ -76,12 +77,13 @@ main = getArgs >>= parse
|
||||||
|
|
||||||
parse :: [String] -> IO ()
|
parse :: [String] -> IO ()
|
||||||
parse [] = run
|
parse [] = run
|
||||||
parse ["--deps"] = printDeps
|
parse ["--deps"] = withCache printDeps
|
||||||
parse _ = usage
|
parse _ = usage
|
||||||
|
|
||||||
run :: IO ()
|
run :: IO ()
|
||||||
run = do
|
run = do
|
||||||
conf <- evalConf =<< connectDBusX
|
db <- connectDBusX
|
||||||
|
conf <- withCache $ evalConf db
|
||||||
ds <- getDirectories
|
ds <- getDirectories
|
||||||
-- IDK why this is necessary; nothing prior to this will print if missing
|
-- IDK why this is necessary; nothing prior to this will print if missing
|
||||||
hFlush stdout
|
hFlush stdout
|
||||||
|
@ -157,29 +159,29 @@ evalConf db = do
|
||||||
startDBusInterfaces = mapM_ (\f -> executeSometimes $ f $ dbSesClient db)
|
startDBusInterfaces = mapM_ (\f -> executeSometimes $ f $ dbSesClient db)
|
||||||
$ fsDBusExporters features
|
$ fsDBusExporters features
|
||||||
startChildDaemons = do
|
startChildDaemons = do
|
||||||
(h, p) <- spawnPipe "xmobar"
|
(h, p) <- io $ spawnPipe "xmobar"
|
||||||
ps <- catMaybes <$> mapM executeSometimes (fsDaemons features)
|
ps <- catMaybes <$> mapM executeSometimes (fsDaemons features)
|
||||||
return (h, ThreadState (p:ps) [h])
|
return (h, ThreadState (p:ps) [h])
|
||||||
startRemovableMon = void $ executeSometimes $ fsRemovableMon features
|
startRemovableMon = void $ executeSometimes $ fsRemovableMon features
|
||||||
$ dbSysClient db
|
$ dbSysClient db
|
||||||
startPowerMon = forkIO_ $ void $ executeSometimes $ fsPowerMon features
|
startPowerMon = void $ fork $ void $ executeSometimes $ fsPowerMon features
|
||||||
startDynWorkspaces = do
|
startDynWorkspaces = do
|
||||||
dws <- catMaybes <$> mapM evalSometimes (fsDynWorkspaces features)
|
dws <- catMaybes <$> mapM evalSometimes (fsDynWorkspaces features)
|
||||||
forkIO_ $ runWorkspaceMon dws
|
io $ forkIO_ $ runWorkspaceMon dws
|
||||||
return dws
|
return dws
|
||||||
|
|
||||||
printDeps :: IO ()
|
printDeps :: FIO ()
|
||||||
printDeps = do
|
printDeps = do
|
||||||
db <- connectDBus
|
db <- io connectDBus
|
||||||
(i, f, d) <- allFeatures db
|
(i, f, d) <- allFeatures db
|
||||||
is <- mapM dumpSometimes i
|
is <- mapM dumpSometimes i
|
||||||
fs <- mapM dumpFeature f
|
fs <- mapM dumpFeature f
|
||||||
ds <- mapM dumpSometimes d
|
ds <- mapM dumpSometimes d
|
||||||
let (UQ u) = jsonArray $ fmap JSON_UQ $ is ++ fs ++ ds
|
let (UQ u) = jsonArray $ fmap JSON_UQ $ is ++ fs ++ ds
|
||||||
putStrLn u
|
io $ putStrLn u
|
||||||
disconnectDBus db
|
io $ disconnectDBus db
|
||||||
|
|
||||||
allFeatures :: DBusState -> IO ([SometimesIO], [FeatureX], [Sometimes DynWorkspace])
|
allFeatures :: DBusState -> FIO ([SometimesIO], [FeatureX], [Sometimes DynWorkspace])
|
||||||
allFeatures db = do
|
allFeatures db = do
|
||||||
let bfs = concatMap (fmap kbMaybeAction . kgBindings)
|
let bfs = concatMap (fmap kbMaybeAction . kgBindings)
|
||||||
$ externalBindings ts db
|
$ externalBindings ts db
|
||||||
|
@ -260,7 +262,7 @@ vmDynamicWorkspace :: Sometimes DynWorkspace
|
||||||
vmDynamicWorkspace = sometimes1 "virtualbox workspace" "windows 8 VM" root
|
vmDynamicWorkspace = sometimes1 "virtualbox workspace" "windows 8 VM" root
|
||||||
where
|
where
|
||||||
root = IORoot_ dw $ And_ (Only_ $ sysExe "VBoxManage")
|
root = IORoot_ dw $ And_ (Only_ $ sysExe "VBoxManage")
|
||||||
$ Only_ $ sysTest name $ vmExists vm
|
$ Only_ $ IOTest_ name $ vmExists vm
|
||||||
name = unwords ["test if", vm, "exists"]
|
name = unwords ["test if", vm, "exists"]
|
||||||
c = "VirtualBoxVM"
|
c = "VirtualBoxVM"
|
||||||
vm = "win8raw"
|
vm = "win8raw"
|
||||||
|
@ -595,13 +597,13 @@ data KeyGroup a = KeyGroup
|
||||||
, kgBindings :: [KeyBinding a]
|
, kgBindings :: [KeyBinding a]
|
||||||
}
|
}
|
||||||
|
|
||||||
evalExternal :: [KeyGroup FeatureX] -> IO [KeyGroup MaybeX]
|
evalExternal :: [KeyGroup FeatureX] -> FIO [KeyGroup MaybeX]
|
||||||
evalExternal = mapM go
|
evalExternal = mapM go
|
||||||
where
|
where
|
||||||
go k@KeyGroup { kgBindings = bs } =
|
go k@KeyGroup { kgBindings = bs } =
|
||||||
(\bs' -> k { kgBindings = bs' }) <$> mapM evalKeyBinding bs
|
(\bs' -> k { kgBindings = bs' }) <$> mapM evalKeyBinding bs
|
||||||
|
|
||||||
evalKeyBinding :: KeyBinding FeatureX -> IO (KeyBinding MaybeX)
|
evalKeyBinding :: KeyBinding FeatureX -> FIO (KeyBinding MaybeX)
|
||||||
evalKeyBinding k@KeyBinding { kbMaybeAction = a } =
|
evalKeyBinding k@KeyBinding { kbMaybeAction = a } =
|
||||||
(\f -> k { kbMaybeAction = f }) <$> evalFeature a
|
(\f -> k { kbMaybeAction = f }) <$> evalFeature a
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{-# LANGUAGE DeriveFunctor #-}
|
{-# LANGUAGE DeriveFunctor #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE GADTs #-}
|
{-# LANGUAGE GADTs #-}
|
||||||
{-# LANGUAGE RankNTypes #-}
|
{-# LANGUAGE RankNTypes #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
@ -51,6 +52,8 @@ module XMonad.Internal.Dependency
|
||||||
, JSONMixed(..)
|
, JSONMixed(..)
|
||||||
|
|
||||||
-- testing
|
-- testing
|
||||||
|
, FIO
|
||||||
|
, withCache
|
||||||
, evalFeature
|
, evalFeature
|
||||||
, executeSometimes
|
, executeSometimes
|
||||||
, executeAlways
|
, executeAlways
|
||||||
|
@ -81,7 +84,7 @@ module XMonad.Internal.Dependency
|
||||||
, pathR
|
, pathR
|
||||||
, pathRW
|
, pathRW
|
||||||
, pathW
|
, pathW
|
||||||
, sysTest
|
-- , sysTest
|
||||||
, voidResult
|
, voidResult
|
||||||
, voidRead
|
, voidRead
|
||||||
|
|
||||||
|
@ -91,11 +94,16 @@ module XMonad.Internal.Dependency
|
||||||
|
|
||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
import Control.Monad.Identity
|
import Control.Monad.Identity
|
||||||
|
import Control.Monad.State
|
||||||
|
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
|
import qualified Data.HashMap.Strict as H
|
||||||
|
import Data.Hashable
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
import DBus
|
import DBus
|
||||||
import DBus.Client
|
import DBus.Client
|
||||||
import DBus.Internal
|
import DBus.Internal
|
||||||
|
@ -116,29 +124,34 @@ import XMonad.Internal.Shell
|
||||||
-- Here we attempt to build and return the monadic actions encoded by each
|
-- Here we attempt to build and return the monadic actions encoded by each
|
||||||
-- feature.
|
-- feature.
|
||||||
|
|
||||||
|
-- | Run feature evaluation(s) with the cache
|
||||||
|
-- Currently there is no easy way to not use this (oh well)
|
||||||
|
withCache :: FIO a -> IO a
|
||||||
|
withCache x = evalStateT x emptyCache
|
||||||
|
|
||||||
-- | Execute an Always immediately
|
-- | Execute an Always immediately
|
||||||
executeAlways :: MonadIO m => Always (m a) -> m a
|
executeAlways :: Always (IO a) -> FIO a
|
||||||
executeAlways = join . evalAlways
|
executeAlways = io <=< evalAlways
|
||||||
|
|
||||||
-- | Execute a Sometimes immediately (or do nothing if failure)
|
-- | Execute a Sometimes immediately (or do nothing if failure)
|
||||||
executeSometimes :: MonadIO m => Sometimes (m a) -> m (Maybe a)
|
executeSometimes :: Sometimes (IO a) -> FIO (Maybe a)
|
||||||
executeSometimes a = maybe (return Nothing) (fmap Just) =<< evalSometimes a
|
executeSometimes a = maybe (return Nothing) (io . fmap Just) =<< evalSometimes a
|
||||||
|
|
||||||
-- | Possibly return the action of an Always/Sometimes
|
-- | Possibly return the action of an Always/Sometimes
|
||||||
evalFeature :: MonadIO m => Feature a -> m (Maybe a)
|
evalFeature :: Feature a -> FIO (Maybe a)
|
||||||
evalFeature (Right a) = Just <$> evalAlways a
|
evalFeature (Right a) = Just <$> evalAlways a
|
||||||
evalFeature (Left s) = evalSometimes s
|
evalFeature (Left s) = evalSometimes s
|
||||||
|
|
||||||
-- | Possibly return the action of a Sometimes
|
-- | Possibly return the action of a Sometimes
|
||||||
evalSometimes :: MonadIO m => Sometimes a -> m (Maybe a)
|
evalSometimes :: Sometimes a -> FIO (Maybe a)
|
||||||
evalSometimes x = io $ either goFail goPass =<< evalSometimesMsg x
|
evalSometimes x = either goFail goPass =<< evalSometimesMsg x
|
||||||
where
|
where
|
||||||
goPass (PostPass a ws) = putErrors ws >> return (Just a)
|
goPass (PostPass a ws) = putErrors ws >> return (Just a)
|
||||||
goFail es = putErrors es >> return Nothing
|
goFail es = putErrors es >> return Nothing
|
||||||
putErrors = mapM_ putStrLn
|
putErrors = io . mapM_ putStrLn
|
||||||
|
|
||||||
-- | Return the action of an Always
|
-- | Return the action of an Always
|
||||||
evalAlways :: MonadIO m => Always a -> m a
|
evalAlways :: Always a -> FIO a
|
||||||
evalAlways a = do
|
evalAlways a = do
|
||||||
(PostPass x ws) <- evalAlwaysMsg a
|
(PostPass x ws) <- evalAlwaysMsg a
|
||||||
io $ mapM_ putStrLn ws
|
io $ mapM_ putStrLn ws
|
||||||
|
@ -148,11 +161,11 @@ evalAlways a = do
|
||||||
-- | Feature status
|
-- | Feature status
|
||||||
|
|
||||||
-- | Dump the status of a Feature
|
-- | Dump the status of a Feature
|
||||||
dumpFeature :: Feature a -> IO JSONUnquotable
|
dumpFeature :: Feature a -> FIO JSONUnquotable
|
||||||
dumpFeature = either dumpSometimes dumpAlways
|
dumpFeature = either dumpSometimes dumpAlways
|
||||||
|
|
||||||
-- | Dump the status of an Always to stdout
|
-- | Dump the status of an Always to stdout
|
||||||
dumpAlways :: Always a -> IO JSONUnquotable
|
dumpAlways :: Always a -> FIO JSONUnquotable
|
||||||
dumpAlways (Always n x) = go [] x
|
dumpAlways (Always n x) = go [] x
|
||||||
where
|
where
|
||||||
go failed (Option o os) = do
|
go failed (Option o os) = do
|
||||||
|
@ -165,7 +178,7 @@ dumpAlways (Always n x) = go [] x
|
||||||
untested acc (Option o os) = untested (dataSubfeatureRoot o:acc) os
|
untested acc (Option o os) = untested (dataSubfeatureRoot o:acc) os
|
||||||
|
|
||||||
-- | Dump the status of a Sometimes to stdout
|
-- | Dump the status of a Sometimes to stdout
|
||||||
dumpSometimes :: Sometimes a -> IO JSONUnquotable
|
dumpSometimes :: Sometimes a -> FIO JSONUnquotable
|
||||||
dumpSometimes (Sometimes n a) = go [] a
|
dumpSometimes (Sometimes n a) = go [] a
|
||||||
where
|
where
|
||||||
go failed [] = return $ jsonSometimes (Q n) Nothing failed []
|
go failed [] = return $ jsonSometimes (Q n) Nothing failed []
|
||||||
|
@ -277,18 +290,29 @@ data DBusDependency_ = Bus BusName
|
||||||
|
|
||||||
-- | A dependency that only requires IO to evaluate (no payload)
|
-- | A dependency that only requires IO to evaluate (no payload)
|
||||||
data IODependency_ = IOSystem_ SystemDependency
|
data IODependency_ = IOSystem_ SystemDependency
|
||||||
|
| IOTest_ String (IO (Maybe String))
|
||||||
| forall a. IOSometimes_ (Sometimes a)
|
| forall a. IOSometimes_ (Sometimes a)
|
||||||
|
|
||||||
|
-- instance Eq IODependency_ where
|
||||||
|
-- (==) (IOSystem_ s0) (IOSystem_ s1) = s0 == s1
|
||||||
|
-- (==) (IOTest_ _ _) (IOTest_ _ _) = False
|
||||||
|
-- (==) (IOSometimes_ _) (IOSometimes_ _) = False
|
||||||
|
-- (==) _ _ = False
|
||||||
|
|
||||||
-- | A system component to an IODependency
|
-- | A system component to an IODependency
|
||||||
-- This name is dumb, but most constructors should be obvious
|
-- This name is dumb, but most constructors should be obvious
|
||||||
data SystemDependency =
|
data SystemDependency =
|
||||||
Executable Bool FilePath
|
Executable Bool FilePath
|
||||||
| AccessiblePath FilePath Bool Bool
|
| AccessiblePath FilePath Bool Bool
|
||||||
| IOTest String (IO (Maybe String))
|
|
||||||
| Systemd UnitType String
|
| Systemd UnitType String
|
||||||
|
deriving (Eq, Show, Generic)
|
||||||
|
|
||||||
|
instance Hashable SystemDependency
|
||||||
|
|
||||||
-- | The type of a systemd service
|
-- | The type of a systemd service
|
||||||
data UnitType = SystemUnit | UserUnit deriving (Eq, Show)
|
data UnitType = SystemUnit | UserUnit deriving (Eq, Show, Generic)
|
||||||
|
|
||||||
|
instance Hashable UnitType
|
||||||
|
|
||||||
-- | Wrapper type to describe and endpoint
|
-- | Wrapper type to describe and endpoint
|
||||||
data DBusMember = Method_ MemberName
|
data DBusMember = Method_ MemberName
|
||||||
|
@ -327,24 +351,52 @@ addMsgs (PostPass a ms) ms' = PostPass a $ ms ++ ms'
|
||||||
-- | An action that failed
|
-- | An action that failed
|
||||||
data PostFail = PostFail [String] | PostMissing String
|
data PostFail = PostFail [String] | PostMissing String
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- | Evaluation cache
|
||||||
|
--
|
||||||
|
-- Setting up trees like this usually entails having some repeated dependencies.
|
||||||
|
-- Testing the same dependency multiple times is stupid, so cache the results.
|
||||||
|
-- Note this is basically memorization, except that the results are IO-dependent
|
||||||
|
-- and this may technically change with each invocation. The assumption here is
|
||||||
|
-- that each repeated test without caching would be run in such close succession
|
||||||
|
-- that the results will always be the same.
|
||||||
|
|
||||||
|
type FIO a = StateT Cache IO a
|
||||||
|
|
||||||
|
newtype Cache = Cache
|
||||||
|
{ cSys :: H.HashMap SystemDependency Result_
|
||||||
|
-- , cIO :: forall p. H.HashMap (IODependency p) (Result p)
|
||||||
|
-- , cIO_ :: H.HashMap IODependency_ Result_
|
||||||
|
-- , cDBus :: H.HashMap DBusDependency_ Result_
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyCache :: Cache
|
||||||
|
emptyCache = Cache H.empty
|
||||||
|
|
||||||
|
memoizeSys :: (SystemDependency -> IO Result_) -> SystemDependency -> FIO Result_
|
||||||
|
memoizeSys f d = do
|
||||||
|
m <- gets cSys
|
||||||
|
let r = H.lookup d m
|
||||||
|
maybe (io $ f d) return r
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Testing pipeline
|
-- | Testing pipeline
|
||||||
|
|
||||||
evalSometimesMsg :: MonadIO m => Sometimes a -> m (Result a)
|
evalSometimesMsg :: Sometimes a -> FIO (Result a)
|
||||||
evalSometimesMsg (Sometimes n xs) = io $ do
|
evalSometimesMsg (Sometimes n xs) = do
|
||||||
PostSometimes { psSuccess = s, psFailed = fs } <- testSometimes xs
|
PostSometimes { psSuccess = s, psFailed = fs } <- testSometimes xs
|
||||||
case s of
|
case s of
|
||||||
(Just (Subfeature { sfData = p })) -> Right . addMsgs p <$> failedMsgs False n fs
|
(Just (Subfeature { sfData = p })) -> Right . addMsgs p <$> failedMsgsIO False n fs
|
||||||
_ -> Left <$> failedMsgs True n fs
|
_ -> Left <$> failedMsgsIO True n fs
|
||||||
|
|
||||||
evalAlwaysMsg :: MonadIO m => Always a -> m (PostPass a)
|
evalAlwaysMsg :: Always a -> FIO (PostPass a)
|
||||||
evalAlwaysMsg (Always n x) = io $ do
|
evalAlwaysMsg (Always n x) = do
|
||||||
r <- testAlways x
|
r <- testAlways x
|
||||||
case r of
|
case r of
|
||||||
(Primary (Subfeature { sfData = p }) fs _) -> addMsgs p <$> failedMsgs False n fs
|
(Primary (Subfeature { sfData = p }) fs _) -> addMsgs p <$> failedMsgsIO False n fs
|
||||||
(Fallback act fs) -> PostPass act <$> failedMsgs False n fs
|
(Fallback act fs) -> PostPass act <$> failedMsgsIO False n fs
|
||||||
|
|
||||||
testAlways :: Always_ a -> IO (PostAlways a)
|
testAlways :: Always_ a -> FIO (PostAlways a)
|
||||||
testAlways = go []
|
testAlways = go []
|
||||||
where
|
where
|
||||||
go failed (Option fd next) = do
|
go failed (Option fd next) = do
|
||||||
|
@ -354,18 +406,18 @@ testAlways = go []
|
||||||
(Right pass) -> return $ Primary pass failed next
|
(Right pass) -> return $ Primary pass failed next
|
||||||
go failed (Always_ ar) = (`Fallback` failed) <$> evalFallbackRoot ar
|
go failed (Always_ ar) = (`Fallback` failed) <$> evalFallbackRoot ar
|
||||||
|
|
||||||
evalFallbackRoot :: FallbackRoot a -> IO a
|
evalFallbackRoot :: FallbackRoot a -> FIO a
|
||||||
evalFallbackRoot (FallbackAlone a) = return a
|
evalFallbackRoot (FallbackAlone a) = return a
|
||||||
evalFallbackRoot (FallbackTree a s) = a <$> evalFallbackStack s
|
evalFallbackRoot (FallbackTree a s) = a <$> evalFallbackStack s
|
||||||
|
|
||||||
evalFallbackStack :: FallbackStack p -> IO p
|
evalFallbackStack :: FallbackStack p -> FIO p
|
||||||
evalFallbackStack (FallbackBottom a) = evalAlways a
|
evalFallbackStack (FallbackBottom a) = evalAlways a
|
||||||
evalFallbackStack (FallbackStack f a as) = do
|
evalFallbackStack (FallbackStack f a as) = do
|
||||||
ps <- evalFallbackStack as
|
ps <- evalFallbackStack as
|
||||||
p <- evalAlways a
|
p <- evalAlways a
|
||||||
return $ f p ps
|
return $ f p ps
|
||||||
|
|
||||||
testSometimes :: Sometimes_ a -> IO (PostSometimes a)
|
testSometimes :: Sometimes_ a -> FIO (PostSometimes a)
|
||||||
testSometimes = go (PostSometimes Nothing [])
|
testSometimes = go (PostSometimes Nothing [])
|
||||||
where
|
where
|
||||||
go ts [] = return ts
|
go ts [] = return ts
|
||||||
|
@ -375,13 +427,13 @@ testSometimes = go (PostSometimes Nothing [])
|
||||||
(Left l) -> go (ts { psFailed = l:psFailed ts }) xs
|
(Left l) -> go (ts { psFailed = l:psFailed ts }) xs
|
||||||
(Right pass) -> return $ ts { psSuccess = Just pass }
|
(Right pass) -> return $ ts { psSuccess = Just pass }
|
||||||
|
|
||||||
testSubfeature :: SubfeatureRoot a -> IO (Either SubfeatureFail (SubfeaturePass a))
|
testSubfeature :: SubfeatureRoot a -> FIO (Either SubfeatureFail (SubfeaturePass a))
|
||||||
testSubfeature sf@Subfeature{ sfData = t } = do
|
testSubfeature sf@Subfeature{ sfData = t } = do
|
||||||
t' <- testRoot t
|
t' <- testRoot t
|
||||||
-- monomorphism restriction :(
|
-- monomorphism restriction :(
|
||||||
return $ bimap (\n -> sf { sfData = n }) (\n -> sf { sfData = n }) t'
|
return $ bimap (\n -> sf { sfData = n }) (\n -> sf { sfData = n }) t'
|
||||||
|
|
||||||
testRoot :: Root a -> IO (Either PostFail (PostPass a))
|
testRoot :: Root a -> FIO (Either PostFail (PostPass a))
|
||||||
testRoot r = do
|
testRoot r = do
|
||||||
case r of
|
case r of
|
||||||
(IORoot a t) -> go a testIODependency_ testIODependency t
|
(IORoot a t) -> go a testIODependency_ testIODependency t
|
||||||
|
@ -391,7 +443,7 @@ testRoot r = do
|
||||||
_ -> return $ Left $ PostMissing "client not available"
|
_ -> return $ Left $ PostMissing "client not available"
|
||||||
where
|
where
|
||||||
-- rank N polymorphism is apparently undecidable...gross
|
-- rank N polymorphism is apparently undecidable...gross
|
||||||
go a f_ (f :: forall q. d q -> IO (Result q)) t =
|
go a f_ (f :: forall q. d q -> FIO (Result q)) t =
|
||||||
bimap PostFail (fmap a) <$> testTree f_ f t
|
bimap PostFail (fmap a) <$> testTree f_ f t
|
||||||
go_ a f_ t = bimap PostFail (PostPass a) <$> testTree_ f_ t
|
go_ a f_ t = bimap PostFail (PostPass a) <$> testTree_ f_ t
|
||||||
|
|
||||||
|
@ -400,11 +452,11 @@ testRoot r = do
|
||||||
|
|
||||||
type Result p = Either [String] (PostPass p)
|
type Result p = Either [String] (PostPass p)
|
||||||
|
|
||||||
testTree :: forall d d_ p. (d_ -> IO Result_) -> (forall q. d q -> IO (Result q))
|
testTree :: forall d d_ p. (d_ -> FIO Result_) -> (forall q. d q -> FIO (Result q))
|
||||||
-> Tree d d_ p -> IO (Either [String] (PostPass p))
|
-> Tree d d_ p -> FIO (Either [String] (PostPass p))
|
||||||
testTree test_ test = go
|
testTree test_ test = go
|
||||||
where
|
where
|
||||||
go :: forall q. Tree d d_ q -> IO (Either [String] (PostPass q))
|
go :: forall q. Tree d d_ q -> FIO (Either [String] (PostPass q))
|
||||||
go (And12 f a b) = do
|
go (And12 f a b) = do
|
||||||
ra <- go a
|
ra <- go a
|
||||||
liftRight (\pa -> (and2nd f pa =<<) <$> go b) ra
|
liftRight (\pa -> (and2nd f pa =<<) <$> go b) ra
|
||||||
|
@ -421,8 +473,8 @@ testTree test_ test = go
|
||||||
and2nd f (PostPass pa wa) (PostPass pb wb) = Right $ PostPass (f pa pb) $ wa ++ wb
|
and2nd f (PostPass pa wa) (PostPass pb wb) = Right $ PostPass (f pa pb) $ wa ++ wb
|
||||||
liftRight = either (return . Left)
|
liftRight = either (return . Left)
|
||||||
|
|
||||||
testIODependency :: IODependency p -> IO (Result p)
|
testIODependency :: IODependency p -> FIO (Result p)
|
||||||
testIODependency (IORead _ t) = t
|
testIODependency (IORead _ t) = io t
|
||||||
testIODependency (IOConst c) = return $ Right $ PostPass c []
|
testIODependency (IOConst c) = return $ Right $ PostPass c []
|
||||||
-- TODO this is a bit odd because this is a dependency that will always
|
-- TODO this is a bit odd because this is a dependency that will always
|
||||||
-- succeed, which kinda makes this pointless. The only reason I would want this
|
-- succeed, which kinda makes this pointless. The only reason I would want this
|
||||||
|
@ -436,7 +488,7 @@ testIODependency (IOSometimes x f) = second (fmap f) <$> evalSometimesMsg x
|
||||||
|
|
||||||
type Result_ = Either [String] [String]
|
type Result_ = Either [String] [String]
|
||||||
|
|
||||||
testTree_ :: (d -> IO Result_) -> Tree_ d -> IO (Either [String] [String])
|
testTree_ :: (d -> FIO Result_) -> Tree_ d -> FIO (Either [String] [String])
|
||||||
testTree_ test = go
|
testTree_ test = go
|
||||||
where
|
where
|
||||||
go (And_ a b) = either (return . Left) (`test2nd` b) =<< go a
|
go (And_ a b) = either (return . Left) (`test2nd` b) =<< go a
|
||||||
|
@ -444,15 +496,15 @@ testTree_ test = go
|
||||||
go (Only_ a) = test a
|
go (Only_ a) = test a
|
||||||
test2nd ws = fmap ((Right . (ws ++)) =<<) . go
|
test2nd ws = fmap ((Right . (ws ++)) =<<) . go
|
||||||
|
|
||||||
testIODependency_ :: IODependency_ -> IO Result_
|
testIODependency_ :: IODependency_ -> FIO Result_
|
||||||
testIODependency_ (IOSystem_ s) = maybe (Right []) (Left . (:[])) <$> testSysDependency s
|
testIODependency_ (IOSystem_ s) = memoizeSys (fmap readResult_ . testSysDependency) s
|
||||||
|
testIODependency_ (IOTest_ _ t) = io $ readResult_ <$> t
|
||||||
testIODependency_ (IOSometimes_ x) = second (\(PostPass _ ws) -> ws) <$> evalSometimesMsg x
|
testIODependency_ (IOSometimes_ x) = second (\(PostPass _ ws) -> ws) <$> evalSometimesMsg x
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | System Dependency Testing
|
-- | System Dependency Testing
|
||||||
|
|
||||||
testSysDependency :: SystemDependency -> IO (Maybe String)
|
testSysDependency :: SystemDependency -> IO (Maybe String)
|
||||||
testSysDependency (IOTest _ t) = t
|
|
||||||
testSysDependency (Executable sys bin) = maybe (Just msg) (const Nothing)
|
testSysDependency (Executable sys bin) = maybe (Just msg) (const Nothing)
|
||||||
<$> findExecutable bin
|
<$> findExecutable bin
|
||||||
where
|
where
|
||||||
|
@ -496,8 +548,8 @@ introspectInterface = interfaceName_ "org.freedesktop.DBus.Introspectable"
|
||||||
introspectMethod :: MemberName
|
introspectMethod :: MemberName
|
||||||
introspectMethod = memberName_ "Introspect"
|
introspectMethod = memberName_ "Introspect"
|
||||||
|
|
||||||
testDBusDependency_ :: Client -> DBusDependency_ -> IO Result_
|
testDBusDependency_ :: Client -> DBusDependency_ -> FIO Result_
|
||||||
testDBusDependency_ client (Bus bus) = do
|
testDBusDependency_ client (Bus bus) = io $ do
|
||||||
ret <- callMethod client queryBus queryPath queryIface queryMem
|
ret <- callMethod client queryBus queryPath queryIface queryMem
|
||||||
return $ case ret of
|
return $ case ret of
|
||||||
Left e -> Left [e]
|
Left e -> Left [e]
|
||||||
|
@ -513,7 +565,7 @@ testDBusDependency_ client (Bus bus) = do
|
||||||
bodyGetNames [v] = fromMaybe [] $ fromVariant v :: [String]
|
bodyGetNames [v] = fromMaybe [] $ fromVariant v :: [String]
|
||||||
bodyGetNames _ = []
|
bodyGetNames _ = []
|
||||||
|
|
||||||
testDBusDependency_ client (Endpoint busname objpath iface mem) = do
|
testDBusDependency_ client (Endpoint busname objpath iface mem) = io $ do
|
||||||
ret <- callMethod client busname objpath introspectInterface introspectMethod
|
ret <- callMethod client busname objpath introspectInterface introspectMethod
|
||||||
return $ case ret of
|
return $ case ret of
|
||||||
Left e -> Left [e]
|
Left e -> Left [e]
|
||||||
|
@ -632,6 +684,10 @@ voidRead (Left []) = Just "unspecified error"
|
||||||
voidRead (Left (e:_)) = Just e
|
voidRead (Left (e:_)) = Just e
|
||||||
voidRead (Right _) = Nothing
|
voidRead (Right _) = Nothing
|
||||||
|
|
||||||
|
readResult_ :: Maybe String -> Result_
|
||||||
|
readResult_ (Just w) = Left [w]
|
||||||
|
readResult_ _ = Right []
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | IO Dependency Constructors
|
-- | IO Dependency Constructors
|
||||||
|
|
||||||
|
@ -662,17 +718,17 @@ sysdUser = sysd UserUnit
|
||||||
sysdSystem :: String -> IODependency_
|
sysdSystem :: String -> IODependency_
|
||||||
sysdSystem = sysd SystemUnit
|
sysdSystem = sysd SystemUnit
|
||||||
|
|
||||||
sysTest :: String -> IO (Maybe String) -> IODependency_
|
-- sysTest :: String -> IO (Maybe String) -> IODependency_
|
||||||
sysTest n = IOSystem_ . IOTest n
|
-- sysTest n = IOSystem_ . IOTest n
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Printing
|
-- | Printing
|
||||||
|
|
||||||
dumpSubfeatureRoot :: SubfeatureRoot a -> IO (JSONUnquotable, Bool)
|
dumpSubfeatureRoot :: SubfeatureRoot a -> FIO (JSONUnquotable, Bool)
|
||||||
dumpSubfeatureRoot Subfeature { sfData = r, sfName = n } =
|
dumpSubfeatureRoot Subfeature { sfData = r, sfName = n } =
|
||||||
first (jsonSubfeature $ Q n) <$> dumpRoot r
|
first (jsonSubfeature $ Q n) <$> dumpRoot r
|
||||||
|
|
||||||
dumpRoot :: Root a -> IO (JSONUnquotable, Bool)
|
dumpRoot :: Root a -> FIO (JSONUnquotable, Bool)
|
||||||
dumpRoot (IORoot _ t) = first jsonIORoot <$>
|
dumpRoot (IORoot _ t) = first jsonIORoot <$>
|
||||||
dumpTree testIODependency testIODependency_ dataIODependency dataIODependency_ t
|
dumpTree testIODependency testIODependency_ dataIODependency dataIODependency_ t
|
||||||
dumpRoot (IORoot_ _ t) = first jsonIORoot <$>
|
dumpRoot (IORoot_ _ t) = first jsonIORoot <$>
|
||||||
|
@ -687,12 +743,12 @@ dumpRoot (DBusRoot _ t Nothing) =
|
||||||
dumpRoot (DBusRoot_ _ t Nothing) =
|
dumpRoot (DBusRoot_ _ t Nothing) =
|
||||||
return (jsonDBusRoot $ dataTree_ dataDBusDependency t, False)
|
return (jsonDBusRoot $ dataTree_ dataDBusDependency t, False)
|
||||||
|
|
||||||
dumpTree :: forall d d_ p. (forall q. d q -> IO (Result q))
|
dumpTree :: forall d d_ p. (forall q. d q -> FIO (Result q))
|
||||||
-> (d_ -> IO Result_) -> (forall q. d q -> DependencyData)
|
-> (d_ -> FIO Result_) -> (forall q. d q -> DependencyData)
|
||||||
-> (d_ -> DependencyData) -> Tree d d_ p -> IO (JSONUnquotable, Bool)
|
-> (d_ -> DependencyData) -> Tree d d_ p -> FIO (JSONUnquotable, Bool)
|
||||||
dumpTree test test_ dd dd_ = go
|
dumpTree test test_ dd dd_ = go
|
||||||
where
|
where
|
||||||
go :: forall q. Tree d d_ q -> IO (JSONUnquotable, Bool)
|
go :: forall q. Tree d d_ q -> FIO (JSONUnquotable, Bool)
|
||||||
go (And12 _ a b) = doAnd go go data' a b
|
go (And12 _ a b) = doAnd go go data' a b
|
||||||
go (And1 a b) = doAnd go dump_' (dataTree_ dd_) a b
|
go (And1 a b) = doAnd go dump_' (dataTree_ dd_) a b
|
||||||
go (And2 a b) = doAnd dump_' go data' a b
|
go (And2 a b) = doAnd dump_' go data' a b
|
||||||
|
@ -712,8 +768,8 @@ dumpTree test test_ dd dd_ = go
|
||||||
let j = jsonAnd sa
|
let j = jsonAnd sa
|
||||||
if ra then first j <$> fb b else return (j $ fb_ b, ra)
|
if ra then first j <$> fb b else return (j $ fb_ b, ra)
|
||||||
|
|
||||||
dumpTree_ :: (d_ -> IO Result_) -> (d_ -> DependencyData) -> Tree_ d_
|
dumpTree_ :: (d_ -> FIO Result_) -> (d_ -> DependencyData) -> Tree_ d_
|
||||||
-> IO (JSONUnquotable, Bool)
|
-> FIO (JSONUnquotable, Bool)
|
||||||
dumpTree_ test_ dd_ = go
|
dumpTree_ test_ dd_ = go
|
||||||
where
|
where
|
||||||
go (And_ a b) = do
|
go (And_ a b) = do
|
||||||
|
@ -773,6 +829,7 @@ dataIODependency_ :: IODependency_ -> DependencyData
|
||||||
dataIODependency_ d = case d of
|
dataIODependency_ d = case d of
|
||||||
(IOSystem_ s) -> dataSysDependency s
|
(IOSystem_ s) -> dataSysDependency s
|
||||||
(IOSometimes_ _) -> (Q "sometimes", [])
|
(IOSometimes_ _) -> (Q "sometimes", [])
|
||||||
|
(IOTest_ desc _) -> (Q "iotest", [("desc", JSON_Q $ Q desc)])
|
||||||
|
|
||||||
dataSysDependency :: SystemDependency -> DependencyData
|
dataSysDependency :: SystemDependency -> DependencyData
|
||||||
dataSysDependency d = first Q $
|
dataSysDependency d = first Q $
|
||||||
|
@ -780,7 +837,6 @@ dataSysDependency d = first Q $
|
||||||
(Executable sys path) -> ("executable", [ ("system", JSON_UQ $ jsonBool sys)
|
(Executable sys path) -> ("executable", [ ("system", JSON_UQ $ jsonBool sys)
|
||||||
, ("path", JSON_Q $ Q path)
|
, ("path", JSON_Q $ Q path)
|
||||||
])
|
])
|
||||||
(IOTest desc _) -> ("iotest", [("desc", JSON_Q $ Q desc)])
|
|
||||||
(AccessiblePath p r w) -> ("path", [ ("path", JSON_Q $ Q p)
|
(AccessiblePath p r w) -> ("path", [ ("path", JSON_Q $ Q p)
|
||||||
, ("readable", JSON_UQ $ jsonBool r)
|
, ("readable", JSON_UQ $ jsonBool r)
|
||||||
, ("writable", JSON_UQ $ jsonBool w)
|
, ("writable", JSON_UQ $ jsonBool w)
|
||||||
|
@ -915,6 +971,9 @@ curly s = "{" ++ s ++ "}"
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- | Other random formatting
|
-- | Other random formatting
|
||||||
|
|
||||||
|
failedMsgsIO :: Bool -> String -> [SubfeatureFail] -> FIO [String]
|
||||||
|
failedMsgsIO err fn = io . failedMsgs err fn
|
||||||
|
|
||||||
failedMsgs :: Bool -> String -> [SubfeatureFail] -> IO [String]
|
failedMsgs :: Bool -> String -> [SubfeatureFail] -> IO [String]
|
||||||
failedMsgs err fn = fmap concat . mapM (failedMsg err fn)
|
failedMsgs err fn = fmap concat . mapM (failedMsg err fn)
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ fontDependency fam =
|
||||||
IORead (unwords ["test if font", singleQuote fam, "exists"]) $ testFont fam
|
IORead (unwords ["test if font", singleQuote fam, "exists"]) $ testFont fam
|
||||||
|
|
||||||
fontDependency_ :: String -> IODependency_
|
fontDependency_ :: String -> IODependency_
|
||||||
fontDependency_ fam = sysTest n $ voidRead <$> testFont fam
|
fontDependency_ fam = IOTest_ n $ voidRead <$> testFont fam
|
||||||
where
|
where
|
||||||
n = unwords ["test if font", singleQuote fam, "exists"]
|
n = unwords ["test if font", singleQuote fam, "exists"]
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ library
|
||||||
, xmonad >= 0.13
|
, xmonad >= 0.13
|
||||||
, xmonad-contrib >= 0.13
|
, xmonad-contrib >= 0.13
|
||||||
, aeson >= 2.0.3.0
|
, aeson >= 2.0.3.0
|
||||||
|
, unordered-containers >= 0.2.16.0
|
||||||
|
, hashable >= 1.3.5.0
|
||||||
ghc-options: -Wall -Werror -Wpartial-fields -fno-warn-missing-signatures
|
ghc-options: -Wall -Werror -Wpartial-fields -fno-warn-missing-signatures
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
@ -65,6 +67,7 @@ executable xmonad
|
||||||
, my-xmonad
|
, my-xmonad
|
||||||
, xmonad >= 0.13
|
, xmonad >= 0.13
|
||||||
, xmonad-contrib >= 0.13
|
, xmonad-contrib >= 0.13
|
||||||
|
, lifted-base >= 0.2.3.12
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall -Werror -Wpartial-fields -fno-warn-missing-signatures -threaded
|
ghc-options: -Wall -Werror -Wpartial-fields -fno-warn-missing-signatures -threaded
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue