Files
mide/app/Main.hs

96 lines
3.3 KiB
Haskell

{-# LANGUAGE DeriveAnyClass,
DeriveGeneric,
DerivingVia,
DuplicateRecordFields,
LambdaCase,
GADTs,
NoFieldSelectors,
OverloadedRecordDot,
RecordWildCards,
OverloadedStrings #-}
module Main (main) where
import WithCli
import Data.Maybe
import Control.Monad.Reader
import Data.Yaml
import GHC.Generics (Generic)
import Data.Functor
import System.Environment.XDG.BaseDir
import Data.HashMap.Lazy (HashMap, insert, alter, toList)
import System.Environment.XDG.DesktopEntry
import qualified Data.ByteString as BS
data Options = Options {
config :: Maybe FilePath,
cache :: Maybe FilePath,
dmenu :: Bool
}
deriving (Show, Generic, HasArguments)
data Config a where
Config :: { cachePath :: a } -> Config a
deriving (Show, Generic, FromJSON, ToJSON)
type Cache = HashMap String Int
data CacheItem = CacheItem { name :: String
, uses :: Int
}
deriving (Show, Generic, FromJSON, ToJSON)
data Item where
AppItem :: DesktopEntry -> Item
DMenuItem :: String -> Item
deriving Show
defaultCachePath :: IO FilePath
defaultCachePath = getUserCacheFile "mide" "cache.yaml"
defaultConfigPath :: IO FilePath
defaultConfigPath = getUserConfigFile "mide" "config.yaml"
type State = (Config FilePath, Cache)
main :: IO ()
main = withCliModified [ AddShortOption "config" 'c',
AddShortOption "dmenu" 'd',
AddOptionHelp "config" "Point to the file containing yaml for configuration",
AddOptionHelp "cache" "Point to the file containing yaml for cache",
AddOptionHelp "dmenu" "Run in dmenu version"
]
run
fixConfig :: Config (Maybe FilePath) -> IO (Config FilePath)
fixConfig Config{..} = maybe (defaultCachePath <&> Config) (pure . Config) cachePath
run :: Options -> IO ()
run opts =
maybe defaultConfigPath pure opts.config >>= \configPath ->
decodeFileEither configPath >>= either (const $ Config . pure <$> defaultCachePath) pure >>= fixConfig >>= \config ->
(decodeFileEither config.cachePath <&> either mempty ((foldr (\p -> insert p.name p.uses) mempty) :: [CacheItem] -> HashMap String Int)) >>= \cache ->
runReaderT ( updateCache =<< execItem =<< presentOptions =<< if opts.dmenu then dmenu else normal) (config,cache)
normal :: (MonadIO m, MonadReader State m) => m [Item]
normal = liftIO getDirectoryEntriesDefault <&> (AppItem <$>)
dmenu :: (MonadIO m, MonadReader State m) => m [Item]
dmenu = liftIO getContents <&> (DMenuItem <$>) . lines
presentOptions :: (MonadIO m, MonadReader State m) => [Item] -> m Item
presentOptions ls = mapM_ (liftIO . print) ls >> pure (head ls)
execItem :: (MonadIO m, MonadReader State m) => Item -> m Item
execItem i = (case i of
AppItem de -> liftIO (print $ deCommand de)
DMenuItem s -> liftIO (putStrLn s)) >> pure i
updateCache :: (MonadIO m, MonadReader State m) => Item -> m ()
updateCache i =
let name = case i of
AppItem d -> deName [] d
DMenuItem s -> s in
ask >>= \(conf, hmap) ->
liftIO . encodeFile conf.cachePath . map (uncurry CacheItem) . toList $
alter (\case Nothing -> pure 1; Just x -> pure (x+1)) name hmap