diff --git a/app/pinentry-rofi.hs b/app/pinentry-rofi.hs index 7257edc..7167c6a 100644 --- a/app/pinentry-rofi.hs +++ b/app/pinentry-rofi.hs @@ -23,7 +23,7 @@ main = do TI.putStrLn "OK Pleased to meet you" pinentryLoop =<< readPinConf -newtype PinConf = PinConf {pcBwName :: String} deriving (Eq, Show) +newtype PinConf = PinConf {pcBwName :: T.Text} deriving (Eq, Show) instance FromJSON PinConf where parseJSON (Object o) = PinConf <$> o .:? "bitwarden-name" .!= "gnupg" @@ -72,7 +72,7 @@ unknownCommand c = TI.putStrLn $ T.append "ERR 275 Unknown command " c getPin :: PinConf -> IO () getPin p = do its <- getItems - let w = (fmap T.pack . password . login) =<< L.find (\i -> pcBwName p == name i) its + let w = (password . login) =<< L.find (\i -> pcBwName p == name i) its maybe err send w where err = TI.putStrLn "ERR 83886179 Operation canceled " diff --git a/app/rofi-autorandr.hs b/app/rofi-autorandr.hs index 77ea760..b5b0947 100644 --- a/app/rofi-autorandr.hs +++ b/app/rofi-autorandr.hs @@ -30,14 +30,14 @@ checkExe cmd = do TI.putStrLn $ T.append "Could not find executable: " $ T.pack cmd exitWith $ ExitFailure 1 -newtype ARClientConf = ARClientConf [String] +newtype ARClientConf = ARClientConf [T.Text] instance RofiConf ARClientConf where defArgs (ARClientConf a) = a runPrompt :: [String] -> IO () runPrompt a = do - let c = ARClientConf a + let c = ARClientConf $ fmap T.pack a staticProfs <- getAutoRandrProfiles runRofiIO c $ selectAction $ @@ -49,19 +49,19 @@ runPrompt a = do mkGroup header = titledGroup header . toRofiActions - . fmap (\s -> (" " ++ s, selectProfile $ T.pack s)) + . fmap (\s -> (T.append " " s, selectProfile s)) -virtProfs :: [String] +virtProfs :: [T.Text] virtProfs = ["off", "common", "clone-largest", "horizontal", "vertical"] -- TODO filter profiles based on which xrandr outputs are actually connected -getAutoRandrProfiles :: IO [String] +getAutoRandrProfiles :: IO [T.Text] getAutoRandrProfiles = do dir <- getAutoRandrDir contents <- listDirectory dir - filterM (doesDirectoryExist . (dir )) contents + (fmap T.pack) <$> filterM (doesDirectoryExist . (dir )) contents -getAutoRandrDir :: IO String +getAutoRandrDir :: IO FilePath getAutoRandrDir = do c <- getXdgDirectory XdgConfig "autorandr" e <- doesDirectoryExist c @@ -70,6 +70,6 @@ getAutoRandrDir = do appendToHome p = ( p) <$> getHomeDirectory selectProfile :: T.Text -> RofiIO ARClientConf () -selectProfile name = do - io $ TI.putStrLn name - io $ void $ spawnProcess "autorandr" ["--change", T.unpack name] +selectProfile name = liftIO $ do + TI.putStrLn name + void $ spawnProcess "autorandr" ["--change", T.unpack name] diff --git a/app/rofi-bt.hs b/app/rofi-bt.hs index 2c94f15..79421ff 100644 --- a/app/rofi-bt.hs +++ b/app/rofi-bt.hs @@ -6,7 +6,6 @@ module Main (main) where import DBus import DBus.Client -import Data.List.Split import qualified Data.Map as M import Data.Maybe import qualified Data.Text.IO as TI @@ -19,7 +18,7 @@ import System.Environment main :: IO () main = getArgs >>= runPrompt -data RofiBTConf = RofiBTConf [String] ObjectPath +data RofiBTConf = RofiBTConf [T.Text] ObjectPath instance RofiConf RofiBTConf where defArgs (RofiBTConf as _) = as @@ -37,7 +36,7 @@ runPrompt args = do getAdapter paths actions client paths adapter = do ras <- getRofiActions client paths - runRofiIO (RofiBTConf args adapter) $ + runRofiIO (RofiBTConf (fmap T.pack args) adapter) $ selectAction $ emptyMenu { groups = [untitledGroup $ toRofiActions ras] @@ -78,8 +77,8 @@ powerAdapterMaybe client = do -- the 'Set' method value = toVariant $ toVariant True -formatDeviceEntry :: Bool -> String -> String -formatDeviceEntry connected name = unwords [prefix connected, name] +formatDeviceEntry :: Bool -> T.Text -> T.Text +formatDeviceEntry connected name = T.unwords [prefix connected, name] where prefix True = "#" prefix False = " " @@ -90,7 +89,7 @@ getAdapter = L.find pathIsAdaptor getDevices :: Client -> [ObjectPath] -> IO [ObjectPath] getDevices client = filterM (getDevicePaired client) . filter pathIsDevice -type ObjectTree = M.Map ObjectPath (M.Map String (M.Map String Variant)) +type ObjectTree = M.Map ObjectPath (M.Map T.Text (M.Map T.Text Variant)) getObjectTree :: Client -> IO ObjectTree getObjectTree client = @@ -104,7 +103,7 @@ getObjectTree client = getDeviceConnected :: Client -> ObjectPath -> IO (Maybe Bool) getDeviceConnected = getDevProperty "Connected" -getDeviceName :: Client -> ObjectPath -> IO (Maybe String) +getDeviceName :: Client -> ObjectPath -> IO (Maybe T.Text) getDeviceName = getDevProperty "Name" getDevicePaired :: Client -> ObjectPath -> IO Bool @@ -126,24 +125,24 @@ pathIsDevice o = case splitPath o of [a, b, c, _] -> pathIsAdaptorPrefix a b c _ -> False -pathIsAdaptorPrefix :: String -> String -> String -> Bool -pathIsAdaptorPrefix a b c = a == "org" && b == "bluez" && "hci" `L.isPrefixOf` c +pathIsAdaptorPrefix :: T.Text -> T.Text -> T.Text -> Bool +pathIsAdaptorPrefix a b c = a == "org" && b == "bluez" && "hci" `T.isPrefixOf` c -splitPath :: ObjectPath -> [String] -splitPath = splitOn "/" . dropWhile (== '/') . formatObjectPath +splitPath :: ObjectPath -> [T.Text] +splitPath = T.split (== '/') . T.dropWhile (== '/') . T.pack . formatObjectPath getClient :: IO (Maybe Client) getClient = either warn (return . Just) =<< try connectSystem where warn e = TI.putStrLn (T.pack $ clientErrorMessage e) >> return Nothing -callDevMethod :: String -> Client -> ObjectPath -> IO () +callDevMethod :: T.Text -> Client -> ObjectPath -> IO () callDevMethod mem client dev = - void $ callBTMethod client dev btDevInterface $ memberName_ mem + void $ callBTMethod client dev btDevInterface $ memberName_ $ T.unpack mem -getDevProperty :: IsVariant a => String -> Client -> ObjectPath -> IO (Maybe a) +getDevProperty :: IsVariant a => T.Text -> Client -> ObjectPath -> IO (Maybe a) getDevProperty mem client dev = - getBTProperty client dev btDevInterface $ memberName_ mem + getBTProperty client dev btDevInterface $ memberName_ $ T.unpack mem callBTMethod :: Client diff --git a/app/rofi-bw.hs b/app/rofi-bw.hs index b992d2e..f6593d6 100644 --- a/app/rofi-bw.hs +++ b/app/rofi-bw.hs @@ -30,17 +30,16 @@ main = runChecks >> getArgs >>= parse -- TODO check if daemon is running when running client parse :: [String] -> IO () parse ["-d", t] = case readMaybe t of Just t' -> runDaemon t'; _ -> usage -parse ("-c" : args) = runClient args +parse ("-c" : args) = runClient $ fmap T.pack args parse _ = usage usage :: IO () usage = TI.putStrLn $ - T.pack $ - joinNewline - [ "daemon mode: rofi-bw -d TIMEOUT" - , "client mode: rofi-bw -c [ROFI-ARGS]" - ] + joinNewline + [ "daemon mode: rofi-bw -d TIMEOUT" + , "client mode: rofi-bw -c [ROFI-ARGS]" + ] runChecks :: IO () runChecks = checkExe "bw" >> checkExe "rofi" diff --git a/app/rofi-dev.hs b/app/rofi-dev.hs index ce9d17c..2849d5e 100644 --- a/app/rofi-dev.hs +++ b/app/rofi-dev.hs @@ -11,7 +11,6 @@ module Main (main) where import Bitwarden.Internal -import Data.List.Split (splitOn) import qualified Data.Text.IO as TI import Data.Typeable import Dhall hiding (maybe, sequence, void) @@ -29,14 +28,13 @@ import System.Environment import System.FilePath.Posix import System.Posix.User (getEffectiveUserName) import System.Process -import Text.Printf main :: IO () main = getArgs >>= parse parse :: [String] -> IO () parse args = case getOpt Permute options args of - (o, n, []) -> runMounts $ L.foldl (flip id) (defaultOpts n) o + (o, n, []) -> runMounts $ L.foldl (flip id) (defaultOpts (fmap T.pack n)) o (_, _, errs) -> TI.putStrLn $ T.pack $ concat errs ++ usageInfo h options where h = "Usage: rofi-dev [OPTIONS] [-- ROFI-OPTIONS]" @@ -58,7 +56,7 @@ options = , Option ['m'] ["mount"] - (ReqArg (\s m -> m {optsAlias = Just s}) "ALIAS") + (ReqArg (\s m -> m {optsAlias = Just $ T.pack s}) "ALIAS") "Mount the device specified by ALIAS directly" , Option ['u'] @@ -69,9 +67,9 @@ options = data Opts = Opts { optsConfig :: Maybe FilePath - , optsAlias :: Maybe String + , optsAlias :: Maybe T.Text , optsUnmount :: Bool - , optsRofiArgs :: [String] + , optsRofiArgs :: [T.Text] } deriving (Show) @@ -163,31 +161,31 @@ dismountAll = do umount :: Mountable a => [a] -> RofiMountIO () umount = mapM_ (`mountMaybe` True) -mountByAlias :: Bool -> String -> RofiMountIO () +mountByAlias :: Bool -> T.Text -> RofiMountIO () mountByAlias unmountFlag alias = do static <- asks mountconfStaticDevs mapM_ (`mountMaybe` unmountFlag) $ configToTree static <$> M.lookup alias static -mkGroup :: [(Header, ProtoAction [String])] -> Maybe (RofiGroup MountConf) +mkGroup :: [(Header, ProtoAction [T.Text])] -> Maybe (RofiGroup MountConf) mkGroup [] = Nothing mkGroup as = let ((h, _) : _) = as - in Just $ titledGroup (show h) $ toRofiActions $ alignEntries $ fmap snd as + in Just $ titledGroup (T.pack $ show h) $ toRofiActions $ alignEntries $ fmap snd as -alignSep :: String +alignSep :: T.Text alignSep = " | " -alignEntries :: [ProtoAction [String]] -> [(String, RofiMountIO ())] +alignEntries :: [ProtoAction [T.Text]] -> [(T.Text, RofiMountIO ())] alignEntries ps = zip (align es) as where (es, as) = L.unzip $ fmap (\(ProtoAction e a) -> (e, a)) ps align = - fmap (L.intercalate alignSep) + fmap (T.intercalate alignSep) . L.transpose . mapToLast pad . L.transpose - pad xs = let m = getMax xs in fmap (\x -> take m (x ++ L.repeat ' ')) xs - getMax = LP.maximum . fmap length + pad xs = let m = getMax xs in fmap (\x -> T.append x (T.replicate (m - T.length x) " ")) xs + getMax = LP.maximum . fmap T.length mapToLast _ [] = [] mapToLast _ [x] = [x] mapToLast f (x : xs) = f x : mapToLast f xs @@ -197,8 +195,8 @@ alignEntries ps = zip (align es) as data MountConf = MountConf { mountconfVolatilePath :: FilePath - , mountconfRofiArgs :: [String] - , mountconfStaticDevs :: M.Map String TreeConfig + , mountconfRofiArgs :: [T.Text] + , mountconfStaticDevs :: M.Map T.Text TreeConfig , mountconfVerbose :: Bool } deriving (Show) @@ -229,13 +227,13 @@ class Mountable a where then (io . notifyMountResult mounted (getLabel dev)) =<< mount dev mountFlag else when verbose notify' where - notify' = io $ notify IconInfo (getLabel dev ++ " already mounted") Nothing + notify' = io $ notify IconInfo (T.append (getLabel dev) " already mounted") Nothing -- | Check if the mounting utilities are present allInstalled :: a -> RofiMountIO Bool -- | Return a string representing the label of the device - getLabel :: a -> String + getLabel :: a -> T.Text -- | Determine if the given type is mounted or not isMounted :: a -> RofiMountIO Bool @@ -254,21 +252,21 @@ class Mountable a where class Mountable a => Actionable a where -- | Return a string to go in the Rofi menu for the given type - fmtEntry :: a -> [String] + fmtEntry :: a -> [T.Text] fmtEntry d = [getLabel d] groupHeader :: a -> Header -- | Given a mountable type, return a rofi action (string to go in the -- Rofi prompt and an action to perform when it is selected) - mkAction :: a -> RofiMountIO (Header, ProtoAction [String]) + mkAction :: a -> RofiMountIO (Header, ProtoAction [T.Text]) mkAction dev = do m <- mountState dev i <- allInstalled dev let h = groupHeader dev let action = when i $ mountMaybe dev $ mountedState m let entry = case fmtEntry dev of - (e : es) -> (mountedPrefix m i ++ e) : es + (e : es) -> (T.append (mountedPrefix m i) e) : es _ -> [] return (h, ProtoAction entry action) where @@ -280,7 +278,7 @@ class Mountable a => Actionable a where mountableToAction :: Actionable a => RofiMountIO [a] - -> RofiMountIO [(Header, ProtoAction [String])] + -> RofiMountIO [(Header, ProtoAction [T.Text])] mountableToAction ms = mapM mkAction =<< ms type RofiMountIO a = RofiIO MountConf a @@ -314,18 +312,18 @@ data ProtoAction a = ProtoAction a (RofiMountIO ()) data MountConfig = MountConfig { mpPath :: FilePath - , mpLabel :: Maybe String + , mpLabel :: Maybe T.Text } deriving (Show, Generic, FromDhall) data BitwardenConfig = BitwardenConfig - { bwKey :: String + { bwKey :: T.Text , bwTries :: Integer } deriving (Show, Generic, FromDhall) newtype SecretConfig = SecretConfig - {secretAttributes :: M.Map String String} + {secretAttributes :: M.Map T.Text T.Text} deriving (Show, Generic, FromDhall) newtype PromptConfig = PromptConfig @@ -339,11 +337,11 @@ data PasswordConfig deriving (Show, Generic, FromDhall) data CIFSOpts = CIFSOpts - { cifsoptsUsername :: Maybe String - , cifsoptsWorkgroup :: Maybe String + { cifsoptsUsername :: Maybe T.Text + , cifsoptsWorkgroup :: Maybe T.Text , cifsoptsUID :: Maybe Integer , cifsoptsGID :: Maybe Integer - , cifsoptsIocharset :: Maybe String + , cifsoptsIocharset :: Maybe T.Text } deriving (Show, Generic, FromDhall) @@ -354,19 +352,19 @@ data DataConfig deriving (Show, Generic, FromDhall) data VeracryptData = VeracryptData - { vcVolume :: String + { vcVolume :: T.Text , vcPassword :: Maybe PasswordConfig } deriving (Show, Generic, FromDhall) data SSHFSData = SSHFSData - { sshfsRemote :: String + { sshfsRemote :: T.Text , sshfsPassword :: Maybe PasswordConfig } deriving (Show, Generic, FromDhall) data CIFSData = CIFSData - { cifsRemote :: String + { cifsRemote :: T.Text , cifsSudo :: Bool , cifsPassword :: Maybe PasswordConfig , cifsOpts :: Maybe CIFSOpts @@ -381,14 +379,14 @@ data DeviceConfig = DeviceConfig data TreeConfig = TreeConfig { tcParent :: DeviceConfig - , tcChildren :: V.Vector String + , tcChildren :: V.Vector T.Text } deriving (Show, Generic, FromDhall) data StaticConfig = StaticConfig - { scTmpPath :: Maybe String + { scTmpPath :: Maybe FilePath , scVerbose :: Maybe Bool - , scDevices :: M.Map String TreeConfig + , scDevices :: M.Map T.Text TreeConfig } deriving (Show, Generic, FromDhall) @@ -430,10 +428,10 @@ instance Actionable (Tree DeviceConfig) where SSHFSConfig {} -> SSHFSHeader VeracryptConfig {} -> VeracryptHeader -configToTree' :: M.Map String TreeConfig -> [StaticConfigTree] +configToTree' :: M.Map T.Text TreeConfig -> [StaticConfigTree] configToTree' devMap = configToTree devMap <$> M.elems devMap -configToTree :: M.Map String TreeConfig -> TreeConfig -> StaticConfigTree +configToTree :: M.Map T.Text TreeConfig -> TreeConfig -> StaticConfigTree configToTree devMap TreeConfig {tcParent = p, tcChildren = c} = Tree p $ fmap go V.toList c where @@ -476,9 +474,9 @@ instance Mountable DeviceConfig where mount DeviceConfig {deviceMount = m, deviceData = d} True = do m' <- getAbsMountpoint m runAndRemoveDir m' $ io $ case d of - CIFSConfig (CIFSData {cifsSudo = s}) -> runMountSudoMaybe s "umount" [m'] - VeracryptConfig _ -> runVeraCrypt ["-d", m'] "" - _ -> runMount "umount" [m'] "" + CIFSConfig (CIFSData {cifsSudo = s}) -> runMountSudoMaybe s "umount" [T.pack m'] + VeracryptConfig _ -> runVeraCrypt ["-d", T.pack m'] "" + _ -> runMount "umount" [T.pack m'] "" allInstalled DeviceConfig {deviceData = devData} = io $ @@ -500,17 +498,17 @@ instance Mountable DeviceConfig where getLabel DeviceConfig { deviceMount = MountConfig {mpPath = p, mpLabel = l} - } = fromMaybe (takeFileName p) l + } = fromMaybe (T.pack $ takeFileName p) l -mountSSHFS :: FilePath -> Maybe PasswordConfig -> String -> IO MountResult +mountSSHFS :: FilePath -> Maybe PasswordConfig -> T.Text -> IO MountResult mountSSHFS mountpoint pwdConfig remote = withPasswordGetter pwdConfig (run ["-o", "password_stdin"]) $ run [] "" where - run other = runMount "sshfs" (other ++ [remote, mountpoint]) + run other = runMount "sshfs" (other ++ [remote, T.pack mountpoint]) mountCIFS :: Bool - -> String + -> T.Text -> FilePath -> Maybe CIFSOpts -> Maybe PasswordConfig @@ -520,30 +518,30 @@ mountCIFS useSudo remote mountpoint opts pwdConfig = where run = runMountSudoMaybe useSudo "mount.cifs" args runPwd p = runMountSudoMaybe' useSudo "mount.cifs" args [("PASSWD", p)] - args = [remote, mountpoint] ++ maybe [] (\o -> ["-o", fromCIFSOpts o]) opts + args = [remote, T.pack mountpoint] ++ maybe [] (\o -> ["-o", fromCIFSOpts o]) opts -fromCIFSOpts :: CIFSOpts -> String -fromCIFSOpts o = L.intercalate "," $ mapMaybe concatMaybe fs +fromCIFSOpts :: CIFSOpts -> T.Text +fromCIFSOpts o = T.intercalate "," $ mapMaybe concatMaybe fs where fs = [ ("username", cifsoptsUsername) , ("workgroup", cifsoptsWorkgroup) - , ("uid", fmap show . cifsoptsUID) - , ("gid", fmap show . cifsoptsGID) + , ("uid", fmap (T.pack . show) . cifsoptsUID) + , ("gid", fmap (T.pack . show) . cifsoptsGID) , ("iocharset", cifsoptsIocharset) ] - concatMaybe (k, f) = (\v -> k ++ "=" ++ v) <$> f o + concatMaybe (k, f) = (\v -> T.concat [k, "=", v]) <$> f o -mountVeracrypt :: FilePath -> Maybe PasswordConfig -> String -> IO MountResult +mountVeracrypt :: FilePath -> Maybe PasswordConfig -> T.Text -> IO MountResult mountVeracrypt mountpoint pwdConfig volume = withPasswordGetter pwdConfig (runVeraCrypt (args ++ ["--stdin"])) $ runVeraCrypt args "" where - args = [volume, mountpoint] + args = [volume, T.pack mountpoint] -- NOTE: the user is assumed to have added themselves to the sudoers file so -- that this command will work -runVeraCrypt :: [String] -> String -> IO MountResult +runVeraCrypt :: [T.Text] -> T.Text -> IO MountResult runVeraCrypt args = runMount "sudo" (defaultArgs ++ args) where defaultArgs = ["/usr/bin/veracrypt", "--text", "--non-interactive"] @@ -560,29 +558,29 @@ veracryptMountState mc = do where -- TODO don't hardcode the tmp directory auxPath = fmap (\i -> "/tmp/.veracrypt_aux_mnt" ++ [i]) . vcIndex - vcIndex spec = case reverse spec of + vcIndex spec = case T.uncons $ T.reverse spec of -- TODO what if I have more than one digit? - (i : _) -> if i `elem` ['0' .. '9'] then Just i else Nothing + Just (i, _) -> if i `elem` ['0' .. '9'] then Just i else Nothing _ -> Nothing getAbsMountpoint :: MountConfig -> RofiMountIO FilePath getAbsMountpoint MountConfig {mpPath = m} = asks $ flip appendRoot m . mountconfVolatilePath -getStaticActions :: RofiMountIO [(Header, ProtoAction [String])] +getStaticActions :: RofiMountIO [(Header, ProtoAction [T.Text])] getStaticActions = mountableToAction $ asks $ configToTree' . mountconfStaticDevs -------------------------------------------------------------------------------- -- Password-getting functions for static devices -type PasswordGetter = IO (Maybe String) +type PasswordGetter = IO (Maybe T.Text) -runSecret :: M.Map String String -> PasswordGetter +runSecret :: M.Map T.Text T.Text -> PasswordGetter runSecret kvs = readCmdSuccess "secret-tool" ("lookup" : kvs') "" where kvs' = concatMap (\(k, v) -> [k, v]) $ M.toList kvs -runBitwarden :: String -> PasswordGetter +runBitwarden :: T.Text -> PasswordGetter runBitwarden pname = ((password . login) <=< L.find (\i -> name i == pname)) <$> getItems @@ -617,7 +615,7 @@ configToPwd (PwdPr p) = flip runPromptLoop readPassword $ promptTries p withPasswordGetter :: Maybe PasswordConfig - -> (String -> IO MountResult) + -> (T.Text -> IO MountResult) -> IO MountResult -> IO MountResult withPasswordGetter (Just pwdConfig) runPwd _ = @@ -633,8 +631,8 @@ withPasswordGetter Nothing _ run = run -- addresses (eg in /dev) and labels. data Removable = Removable - { removablePath :: String - , removableLabel :: String + { removablePath :: T.Text + , removableLabel :: T.Text } deriving (Eq, Show) @@ -664,42 +662,42 @@ instance Actionable Removable where -- the device getRemovableDevices :: RofiIO c [Removable] getRemovableDevices = - fromLines toDev . lines + fromLines toDev . T.lines . T.pack <$> io (readProcess "lsblk" ["-n", "-r", "-o", columns] "") where columns = "FSTYPE,HOTPLUG,PATH,LABEL,SIZE" -- can't use 'words' here since it will drop spaces in the front - toDev line = case splitBy ' ' line of + toDev line = case T.split (== ' ') line of ("" : _) -> Nothing - [_, "1", d, "", s] -> mk d $ s ++ " Volume" + [_, "1", d, "", s] -> mk d $ T.append s " Volume" [_, "1", d, l, _] -> mk d l _ -> Nothing mk d l = Just $ Removable {removablePath = d, removableLabel = l} -getRemovableActions :: RofiMountIO [(Header, ProtoAction [String])] +getRemovableActions :: RofiMountIO [(Header, ProtoAction [T.Text])] getRemovableActions = mountableToAction getRemovableDevices -------------------------------------------------------------------------------- -- MTP devices -mtpExe :: String +mtpExe :: FilePath mtpExe = "jmtpfs" data MTPFS = MTPFS - { mtpfsBus :: String - , mtpfsDevice :: String + { mtpfsBus :: T.Text + , mtpfsDevice :: T.Text , mtpfsMountpoint :: FilePath - , mtpfsDescription :: String + , mtpfsDescription :: T.Text } deriving (Eq, Show) instance Mountable MTPFS where mount MTPFS {mtpfsBus = b, mtpfsDevice = n, mtpfsMountpoint = m} False = do -- TODO add autodismount to options - let dev = "-device=" ++ b ++ "," ++ n - withTmpMountDir m $ io $ runMount mtpExe [dev, m] "" + let dev = T.concat ["-device=", b, ",", n] + withTmpMountDir m $ io $ runMount (T.pack mtpExe) [dev, T.pack m] "" mount MTPFS {mtpfsMountpoint = m} True = - runAndRemoveDir m $ io $ runMount "umount" [m] "" + runAndRemoveDir m $ io $ runMount "umount" [T.pack m] "" -- \| return True always since the list won't even show without jmtpfs allInstalled _ = return True @@ -719,30 +717,30 @@ getMTPDevices = do go = do dir <- asks mountconfVolatilePath res <- io $ readProcess mtpExe ["-l"] "" - return $ fromLines (toDev dir) $ toDevList res + return $ fromLines (toDev dir) $ toDevList $ T.pack res toDevList = reverse - . takeWhile (not . L.isPrefixOf "Available devices") - . reverse - . lines - toDev dir s = case splitOn ", " s of + . L.takeWhile (not . T.isPrefixOf "Available devices") + . L.reverse + . T.lines + toDev dir s = case L.filter (== " ") $ T.split (== ',') s of [busNum, devNum, _, _, desc, vendor] -> - let d = unwords [vendor, desc] + let d = T.unwords [vendor, desc] in Just $ MTPFS { mtpfsBus = busNum , mtpfsDevice = devNum - , mtpfsMountpoint = dir canonicalize d + , mtpfsMountpoint = dir canonicalize (T.unpack d) , mtpfsDescription = d } _ -> Nothing canonicalize = mapMaybe repl repl c - | c `elem` ("\"*/:<>?\\|" :: String) = Nothing + | c `elem` ("\"*/:<>?\\|" :: [Char]) = Nothing | c == ' ' = Just '-' | otherwise = Just c -getMTPActions :: RofiMountIO [(Header, ProtoAction [String])] +getMTPActions :: RofiMountIO [(Header, ProtoAction [T.Text])] getMTPActions = mountableToAction getMTPDevices mtpExeInstalled :: IO Bool @@ -762,39 +760,40 @@ instance Show NotifyIcon where show IconError = "dialog-error-symbolic" show IconInfo = "dialog-information-symbolic" -notifyMountResult :: Bool -> String -> MountResult -> IO () +notifyMountResult :: Bool -> T.Text -> MountResult -> IO () notifyMountResult mounted label result = case result of - MountError e -> notify IconError (printf "Failed to %s %s" verb label) $ Just e - MountSuccess -> notify IconInfo (printf "Successfully %sed %s" verb label) Nothing + MountError e -> notify IconError (T.unwords ["Failed", "to", verb, label]) $ Just e + MountSuccess -> notify IconInfo (T.concat ["Successfully ", verb, "ed ", label]) Nothing where - verb = if mounted then "unmount" else "mount" :: String + verb = if mounted then "unmount" else "mount" :: T.Text -notify :: NotifyIcon -> String -> Maybe String -> IO () +notify :: NotifyIcon -> T.Text -> Maybe T.Text -> IO () notify icon summary body = void $ spawnProcess "notify-send" $ - maybe args (\b -> args ++ [b]) body + maybe args (\b -> args ++ [b]) $ + fmap T.unpack body where - args = ["-i", show icon, summary] + args = ["-i", show icon, T.unpack summary] -------------------------------------------------------------------------------- -- Mount commands -data MountResult = MountSuccess | MountError String deriving (Show, Eq) +data MountResult = MountSuccess | MountError T.Text deriving (Show, Eq) -runMount :: String -> [String] -> String -> IO MountResult +runMount :: T.Text -> [T.Text] -> T.Text -> IO MountResult runMount cmd args stdin_ = eitherToMountResult <$> readCmdEither cmd args stdin_ -runMount' :: String -> [String] -> String -> [(String, String)] -> IO MountResult +runMount' :: T.Text -> [T.Text] -> T.Text -> [(T.Text, T.Text)] -> IO MountResult runMount' cmd args stdin_ environ = eitherToMountResult <$> readCmdEither' cmd args stdin_ environ -runMountSudoMaybe :: Bool -> String -> [String] -> IO MountResult +runMountSudoMaybe :: Bool -> T.Text -> [T.Text] -> IO MountResult runMountSudoMaybe useSudo cmd args = runMountSudoMaybe' useSudo cmd args [] -runMountSudoMaybe' :: Bool -> String -> [String] -> [(String, String)] -> IO MountResult +runMountSudoMaybe' :: Bool -> T.Text -> [T.Text] -> [(T.Text, T.Text)] -> IO MountResult runMountSudoMaybe' useSudo cmd args environ = maybe (runMount' cmd args "" environ) @@ -802,38 +801,38 @@ runMountSudoMaybe' useSudo cmd args environ = =<< if useSudo then readPassword' "Sudo Password" else return Nothing -- TODO untested --- runSudoMount :: String -> String -> [String] -> String -> IO MountResult +-- runSudoMount :: T.Text -> T.Text -> [T.Text] -> T.Text -> IO MountResult -- runSudoMount rootpass cmd args stdin = runSudoMount' rootpass cmd args stdin [] -runSudoMount' :: String -> String -> [String] -> [(String, String)] -> IO MountResult +runSudoMount' :: T.Text -> T.Text -> [T.Text] -> [(T.Text, T.Text)] -> IO MountResult runSudoMount' rootpass cmd args environ = runMount "sudo" args' rootpass where args' = ["-S"] ++ environ' ++ [cmd] ++ args - environ' = fmap (\(k, v) -> k ++ "=" ++ v) environ + environ' = fmap (\(k, v) -> T.concat [k, "=", v]) environ -eitherToMountResult :: Either (Int, String, String) String -> MountResult +eitherToMountResult :: Either (Int, T.Text, T.Text) T.Text -> MountResult eitherToMountResult (Right _) = MountSuccess eitherToMountResult (Left (_, _, e)) = MountError e -------------------------------------------------------------------------------- -- Low-level mount functions -mountMap :: IO (M.Map FilePath String) +mountMap :: IO (M.Map FilePath T.Text) mountMap = do parseFile <$> readFileUtf8 "/proc/mounts" where parseFile = M.fromList . mapMaybe (parseLine . T.words) . T.lines -- none of these should fail since this file format will never change - parseLine [spec, mountpoint, _, _, _, _] = Just (T.unpack mountpoint, T.unpack spec) + parseLine [spec, mountpoint, _, _, _, _] = Just (T.unpack mountpoint, spec) parseLine _ = Nothing -curDeviceSpecs :: IO [String] +curDeviceSpecs :: IO [T.Text] curDeviceSpecs = M.elems <$> mountMap -curMountpoints :: IO [String] +curMountpoints :: IO [FilePath] curMountpoints = M.keys <$> mountMap -lookupSpec :: FilePath -> IO (Maybe String) +lookupSpec :: FilePath -> IO (Maybe T.Text) lookupSpec mountpoint = M.lookup mountpoint <$> mountMap -- ASSUME the base mount path will always be created because @@ -859,7 +858,7 @@ withTmpMountDir m = -- | Run an unmount command and remove the mountpoint if no errors occur runAndRemoveDir :: FilePath -> RofiMountIO MountResult -> RofiMountIO MountResult runAndRemoveDir m f = do - res <- catch f (return . MountError . (displayException :: SomeException -> String)) + res <- catch f (return . MountError . (T.pack . displayException :: SomeException -> T.Text)) when (res == MountSuccess) $ rmDirMaybe m return res @@ -892,17 +891,17 @@ isDirMounted fp = elem fp <$> curMountpoints -------------------------------------------------------------------------------- -- Other functions -fromLines :: (String -> Maybe a) -> [String] -> [a] +fromLines :: (T.Text -> Maybe a) -> [T.Text] -> [a] fromLines f = mapMaybe (f . stripWS) -- TODO this exists somewhere... -splitBy :: Char -> String -> [String] -splitBy delimiter = foldr f [[]] - where - f _ [] = [] - f c l@(x : xs) - | c == delimiter = [] : l - | otherwise = (c : x) : xs +-- splitBy :: Char -> T.Text -> [T.Text] +-- splitBy delimiter = T.foldr f [[]] +-- where +-- f _ [] = [] +-- f c l@(x : xs) +-- | c == delimiter = [] : l +-- | otherwise = (c : x) : xs appendRoot :: FilePath -> FilePath -> FilePath appendRoot root path = if isRelative path then root path else path diff --git a/app/rofi-evpn.hs b/app/rofi-evpn.hs index ba79dc0..2ab6f62 100644 --- a/app/rofi-evpn.hs +++ b/app/rofi-evpn.hs @@ -4,10 +4,9 @@ module Main (main) where -import Data.List (isPrefixOf) -import Data.List.Split import Data.Maybe import RIO +import qualified RIO.Text as T import Rofi.Command import System.Environment import System.Process @@ -23,7 +22,7 @@ runPrompt args = do run (VPNStatus connected servers) = do let d = getDisconnectAction <$> connected let cs = fmap (getConnectAction connected) servers - runRofiIO (RofiVPNConf args) $ + runRofiIO (RofiVPNConf $ fmap T.pack args) $ selectAction $ emptyMenu { groups = @@ -33,16 +32,16 @@ runPrompt args = do , prompt = Just "Select Action" } -newtype RofiVPNConf = RofiVPNConf [String] +newtype RofiVPNConf = RofiVPNConf [T.Text] instance RofiConf RofiVPNConf where defArgs (RofiVPNConf as) = as type VPNAction = RofiAction RofiVPNConf -type VPNServer = (String, String) +type VPNServer = (T.Text, T.Text) -data VPNStatus = VPNStatus (Maybe String) [VPNServer] deriving (Show) +data VPNStatus = VPNStatus (Maybe T.Text) [VPNServer] deriving (Show) getServers :: IO (Maybe VPNStatus) getServers = do @@ -56,13 +55,13 @@ getStatus = do connected <- getConnectedServer VPNStatus connected <$> getAvailableServers -getConnectedServer :: IO (Maybe String) +getConnectedServer :: IO (Maybe T.Text) getConnectedServer = (procStatus =<<) <$> readCmdSuccess eVPN ["status"] "" where - procStatus = listToMaybe . mapMaybe procLine . lines - procLine l = case words l of + procStatus = listToMaybe . mapMaybe procLine . T.lines + procLine l = case T.words l of -- the output is green... - ("\ESC[1;32;49mConnected" : "to" : server) -> Just $ unwords server + ("\ESC[1;32;49mConnected" : "to" : server) -> Just $ T.unwords server _ -> Nothing getAvailableServers :: IO [VPNServer] @@ -76,13 +75,13 @@ getAvailableServers = procOut =<< readCmdSuccess eVPN ["ls"] "" -- by a blank line, after which there is more stuff I don't care about procOut (Just ls) = return $ - mapMaybe (matchLine . splitOn "\t") $ + mapMaybe (matchLine . T.split (== '\t')) $ takeWhile (/= "") $ drop 1 -- super lame way of matching lines that start with "-----" $ - dropWhile (not . isPrefixOf "-----") $ - lines ls + dropWhile (not . T.isPrefixOf "-----") $ + T.lines ls -- The output of this command is very strange; it is delimited (kinda) by -- tabs but some lines are long enough that they don't have a tab. In -- whatever case, splitting by tabs leads to variable length lists, and the @@ -96,11 +95,11 @@ getAvailableServers = procOut =<< readCmdSuccess eVPN ["ls"] "" daemonIsRunning :: IO Bool daemonIsRunning = isJust <$> readCmdSuccess "pgrep" [eVPND] "" -getDisconnectAction :: String -> VPNAction +getDisconnectAction :: T.Text -> VPNAction getDisconnectAction server = - ("Disconnect from " ++ server, io $ void $ disconnect server) + (T.append "Disconnect from " server, io $ void $ disconnect server) -getConnectAction :: Maybe String -> VPNServer -> VPNAction +getConnectAction :: Maybe T.Text -> VPNServer -> VPNAction getConnectAction connected server = (formatServerLine server, io $ go connected) where @@ -110,15 +109,15 @@ getConnectAction connected server = go _ = con con = connect server -formatServerLine :: VPNServer -> String -formatServerLine (sid, sname) = pad sid ++ " | " ++ sname +formatServerLine :: VPNServer -> T.Text +formatServerLine (sid, sname) = T.concat [pad sid, " | ", sname] where - pad s = s ++ replicate (10 - length s) ' ' + pad s = T.append s $ T.replicate (10 - T.length s) " " -eVPN :: String +eVPN :: T.Text eVPN = "expressvpn" -eVPND :: String +eVPND :: T.Text eVPND = "expressvpnd" connect :: VPNServer -> IO () @@ -126,19 +125,19 @@ connect (sid, sname) = do res <- readCmdSuccess' eVPN ["connect", sid] notifyIf res - ("connected to " ++ sname) - ("failed to connect to " ++ sname) + (T.append "connected to " sname) + (T.append "failed to connect to " sname) -disconnect :: String -> IO Bool +disconnect :: T.Text -> IO Bool disconnect server = do res <- readCmdSuccess' eVPN ["disconnect"] notifyIf res - ("disconnected from " ++ server) - ("failed to disconnect from " ++ server) + (T.append "disconnected from " server) + (T.append "failed to disconnect from " server) return res -readCmdSuccess' :: String -> [String] -> IO Bool +readCmdSuccess' :: T.Text -> [T.Text] -> IO Bool readCmdSuccess' cmd args = isJust <$> readCmdSuccess cmd args "" -- TODO not DRY @@ -148,12 +147,12 @@ instance Show NotifyIcon where show IconError = "dialog-error-symbolic" show IconInfo = "dialog-information-symbolic" -notifyIf :: Bool -> String -> String -> IO () +notifyIf :: Bool -> T.Text -> T.Text -> IO () notifyIf True s _ = notify IconInfo s notifyIf False _ s = notify IconError s -notify :: NotifyIcon -> String -> IO () -notify icon body = void $ spawnProcess "notify-send" $ args ++ [body] +notify :: NotifyIcon -> T.Text -> IO () +notify icon body = void $ spawnProcess "notify-send" $ args ++ [T.unpack body] where args = ["-i", show icon, summary] summary = "ExpressVPN" diff --git a/app/rofi.hs b/app/rofi.hs index 60bcc0b..ed19a39 100644 --- a/app/rofi.hs +++ b/app/rofi.hs @@ -23,12 +23,12 @@ module Main (main) where -import Data.Maybe import Graphics.X11.Types import Graphics.X11.Xlib import Graphics.X11.Xlib.Extras import Graphics.X11.Xrandr import RIO hiding (Display) +import qualified RIO.Text as T import System.Environment import System.Process @@ -37,11 +37,11 @@ main = do r <- getMonitorName let pre = maybe [] (\n -> ["-m", n]) r args <- getArgs - callProcess "/usr/bin/rofi" $ pre ++ args + callProcess "/usr/bin/rofi" $ (fmap T.unpack pre) ++ args data Coord = Coord Int Int deriving (Eq, Show) -getMonitorName :: IO (Maybe String) +getMonitorName :: IO (Maybe T.Text) getMonitorName = do dpy <- openDisplay "" root <- rootWindow dpy $ defaultScreen dpy @@ -62,7 +62,7 @@ getDesktopViewports dpy root = pairs' acc (x1 : x2 : xs) = pairs' (Coord x1 x2 : acc) xs pairs' acc _ = acc -getOutputs :: Display -> Window -> IO [(Coord, String)] +getOutputs :: Display -> Window -> IO [(Coord, T.Text)] getOutputs dpy root = xrrGetScreenResourcesCurrent dpy root >>= maybe (return []) resourcesToCells @@ -79,7 +79,7 @@ getOutputs dpy root = , xrr_oi_crtc = c } ) = do - fmap (\i -> (toCoord i, n)) <$> xrrGetCrtcInfo dpy r c + fmap (\i -> (toCoord i, T.pack n)) <$> xrrGetCrtcInfo dpy r c infoToCell _ _ = return Nothing toCoord c = Coord (fromIntegral $ xrr_ci_x c) (fromIntegral $ xrr_ci_y c) @@ -90,8 +90,8 @@ infix 9 !!? | i < 0 = Nothing | otherwise = listToMaybe $ drop i xs -getAtom32 :: Display -> Window -> String -> IO [Int] +getAtom32 :: Display -> Window -> T.Text -> IO [Int] getAtom32 dpy root str = do - a <- internAtom dpy str False + a <- internAtom dpy (T.unpack str) False p <- getWindowProperty32 dpy a root return $ maybe [] (fmap fromIntegral) p diff --git a/lib/Bitwarden/Internal.hs b/lib/Bitwarden/Internal.hs index 2e38399..3786fca 100644 --- a/lib/Bitwarden/Internal.hs +++ b/lib/Bitwarden/Internal.hs @@ -12,7 +12,6 @@ where import DBus import DBus.Client import Data.Aeson -import Data.String import qualified Data.Text.IO as TI import Data.UnixTime import GHC.Generics @@ -39,7 +38,7 @@ newtype BWServerConf = BWServerConf -- TODO add a cache so the browse list will load faster data CurrentSession = CurrentSession { timestamp :: UnixTime - , hash :: String + , hash :: T.Text } type Session = MVar (Maybe CurrentSession) @@ -62,7 +61,7 @@ syncSession conf ses = notify =<< fmap join . mapM cmd =<< getSession' conf ses let j = isJust res in notifyStatus j $ if j then "sync succeeded" else "sync failed" -getSession' :: BWServerConf -> Session -> IO (Maybe String) +getSession' :: BWServerConf -> Session -> IO (Maybe T.Text) getSession' BWServerConf {timeout = t} ses = do ut <- getUnixTime modifyMVar ses $ \s -> case s of @@ -78,15 +77,15 @@ getSession' BWServerConf {timeout = t} ses = do ut <- getUnixTime return CurrentSession {timestamp = ut, hash = h} -getSession :: BWServerConf -> Session -> IO String +getSession :: BWServerConf -> Session -> IO T.Text getSession conf ses = fromMaybe "" <$> getSession' conf ses -readSession :: String -> IO (Maybe String) +readSession :: T.Text -> IO (Maybe T.Text) readSession pwd = readCmdSuccess "bw" ["unlock", pwd, "--raw"] "" -notifyStatus :: Bool -> String -> IO () +notifyStatus :: Bool -> T.Text -> IO () notifyStatus succeeded msg = - void $ spawnProcess "notify-send" ["-i", i, msg] + void $ spawnProcess "notify-send" ["-i", i, T.unpack msg] where i = if succeeded @@ -109,12 +108,12 @@ notifyStatus succeeded msg = -- - username (if applicable) -> copy to clipboard -- - password (if applicable) -> copy to clipboard -- - anything else (notes and such) -> copy to clipboard -newtype BWClientConf = BWClientConf [String] +newtype BWClientConf = BWClientConf [T.Text] instance RofiConf BWClientConf where defArgs (BWClientConf a) = a -runClient :: [String] -> IO () +runClient :: [T.Text] -> IO () runClient a = do let c = BWClientConf a runRofiIO c $ @@ -136,9 +135,9 @@ browseLogins = io getItems >>= selectItem getItems :: IO [Item] getItems = maybe (return []) getItems' =<< callGetSession -getItems' :: String -> IO [Item] +getItems' :: T.Text -> IO [Item] getItems' session = do - items <- io $ readProcess "bw" ["list", "items", "--session", session] "" + items <- io $ readProcess "bw" ["list", "items", "--session", T.unpack session] "" return $ filter notEmpty $ fromMaybe [] $ decode $ fromString items where notEmpty Item {login = Login {username = Nothing, password = Nothing}} = @@ -146,7 +145,7 @@ getItems' session = do notEmpty _ = True data Item = Item - { name :: String + { name :: T.Text , login :: Login } deriving (Show) @@ -159,8 +158,8 @@ instance FromJSON Item where parseJSON _ = mzero data Login = Login - { username :: Maybe String - , password :: Maybe String + { username :: Maybe T.Text + , password :: Maybe T.Text } deriving (Show, Generic) @@ -188,7 +187,7 @@ selectCopy l = , hotkeys = [copyHotkey, backHotkey] } where - copy = io . setClipboardString + copy = io . setClipboardString . T.unpack copyRepeat s = copy s >> selectCopy l copyHotkey = Hotkey @@ -207,22 +206,27 @@ selectCopy l = keyActions = loginToRofiActions l (const browseLogins) } -loginToRofiActions :: Login -> (String -> RofiIO c ()) -> RofiActions c +loginToRofiActions :: Login -> (T.Text -> RofiIO c ()) -> RofiActions c loginToRofiActions Login {username = u, password = p} a = toRofiActions $ catMaybes [user, pwd] where copyIfJust f = fmap $ liftM2 (,) f a - fmtUsername s = "Username (" ++ s ++ ")" - fmtPassword s = "Password (" ++ take 32 (replicate (length s) '*') ++ ")" + fmtUsername s = T.concat ["Username (", s, ")"] + fmtPassword s = T.concat ["Password (", T.take 32 (T.replicate (T.length s) "*"), ")"] user = copyIfJust fmtUsername u pwd = copyIfJust fmtPassword p -getItemPassword' :: BWServerConf -> Session -> String -> IO (Maybe String) +getItemPassword' :: BWServerConf -> Session -> T.Text -> IO (Maybe T.Text) getItemPassword' conf session item = mapM getPwd =<< getSession' conf session where - getPwd s = readProcess "bw" ["get", "password", item, "--session", s] "" + getPwd s = + T.pack + <$> readProcess + "bw" + ["get", "password", T.unpack item, "--session", T.unpack s] + "" -getItemPassword :: BWServerConf -> Session -> String -> IO String +getItemPassword :: BWServerConf -> Session -> T.Text -> IO T.Text getItemPassword conf session item = fromMaybe "" <$> getItemPassword' conf session item @@ -283,13 +287,13 @@ callLockSession = void $ callMember memLockSession callSyncSession :: IO () callSyncSession = void $ callMember memSyncSession -callGetSession :: IO (Maybe String) +callGetSession :: IO (Maybe T.Text) callGetSession = getBodyString <$> callMember memGetSession -- TODO maybe will need to add a caller for getItemPassword -getBodyString :: [Variant] -> Maybe String -getBodyString [b] = case fromVariant b :: Maybe String of +getBodyString :: [Variant] -> Maybe T.Text +getBodyString [b] = case fromVariant b :: Maybe T.Text of Just "" -> Nothing s -> s getBodyString _ = Nothing diff --git a/lib/Rofi/Command.hs b/lib/Rofi/Command.hs index b8c2d32..c60b67e 100644 --- a/lib/Rofi/Command.hs +++ b/lib/Rofi/Command.hs @@ -25,60 +25,58 @@ module Rofi.Command ) where -import Control.Monad.IO.Unlift -import Control.Monad.Reader import Data.Char import qualified Data.Map.Ordered as M -import Data.Maybe import RIO import qualified RIO.List as L +import qualified RIO.Text as T import System.Process class RofiConf c where - defArgs :: c -> [String] + defArgs :: c -> [T.Text] -type RofiAction c = (String, RofiIO c ()) +type RofiAction c = (T.Text, RofiIO c ()) -type RofiActions c = M.OMap String (RofiIO c ()) +type RofiActions c = M.OMap T.Text (RofiIO c ()) data RofiGroup c = RofiGroup { actions :: RofiActions c - , title :: Maybe String + , title :: Maybe T.Text } untitledGroup :: RofiActions c -> RofiGroup c untitledGroup a = RofiGroup {actions = a, title = Nothing} -titledGroup :: String -> RofiActions c -> RofiGroup c +titledGroup :: T.Text -> RofiActions c -> RofiGroup c titledGroup t a = (untitledGroup a) {title = Just t} data Hotkey c = Hotkey - { keyCombo :: String + { keyCombo :: !T.Text , -- only 1-10 are valid - keyIndex :: Int - , keyDescription :: String + keyIndex :: !Int + , keyDescription :: !T.Text , keyActions :: RofiActions c } -hotkeyBinding :: Hotkey c -> [String] +hotkeyBinding :: Hotkey c -> [T.Text] hotkeyBinding Hotkey {keyIndex = e, keyCombo = c} = [k, c] where - k = "-kb-custom-" ++ show e + k = T.append "-kb-custom-" $ T.pack $ show e -hotkeyMsg1 :: Hotkey c -> String +hotkeyMsg1 :: Hotkey c -> T.Text hotkeyMsg1 Hotkey {keyCombo = c, keyDescription = d} = - c ++ ": " ++ d ++ "" + T.concat [c, ": ", d, ""] -hotkeyMsg :: [Hotkey c] -> [String] +hotkeyMsg :: [Hotkey c] -> [T.Text] hotkeyMsg [] = [] -hotkeyMsg hs = ["-mesg", L.intercalate " | " $ fmap hotkeyMsg1 hs] +hotkeyMsg hs = ["-mesg", T.intercalate " | " $ fmap hotkeyMsg1 hs] -hotkeyArgs :: [Hotkey c] -> [String] +hotkeyArgs :: [Hotkey c] -> [T.Text] hotkeyArgs hks = hotkeyMsg hks ++ concatMap hotkeyBinding hks data RofiMenu c = RofiMenu { groups :: [RofiGroup c] - , prompt :: Maybe String + , prompt :: Maybe T.Text , hotkeys :: [Hotkey c] } @@ -99,27 +97,27 @@ io = liftIO runRofiIO :: c -> RofiIO c a -> IO a runRofiIO c (RofiIO r) = runReaderT r c -toRofiActions :: [(String, RofiIO c ())] -> RofiActions c +toRofiActions :: [(T.Text, RofiIO c ())] -> RofiActions c toRofiActions = M.fromList -rofiActionKeys :: RofiActions c -> String +rofiActionKeys :: RofiActions c -> T.Text rofiActionKeys = joinNewline . map fst . M.assocs -lookupRofiAction :: String -> RofiActions c -> RofiIO c () +lookupRofiAction :: T.Text -> RofiActions c -> RofiIO c () lookupRofiAction key ras = fromMaybe (return ()) $ M.lookup key ras -groupEntries :: RofiGroup c -> String +groupEntries :: RofiGroup c -> T.Text groupEntries RofiGroup {actions = a, title = t} | null a = "" - | otherwise = title' ++ rofiActionKeys a + | otherwise = T.append title' $ rofiActionKeys a where - title' = maybe "" (++ "\n") t + title' = maybe "" (`T.append` "\n") t menuActions :: RofiMenu c -> RofiActions c menuActions = L.foldr (M.<>|) M.empty . fmap actions . groups -menuEntries :: RofiMenu c -> String -menuEntries = L.intercalate "\n\n" . filter (not . null) . fmap groupEntries . groups +menuEntries :: RofiMenu c -> T.Text +menuEntries = T.intercalate "\n\n" . filter (not . T.null) . fmap groupEntries . groups selectAction :: RofiConf c => RofiMenu c -> RofiIO c () selectAction rm = do @@ -133,63 +131,67 @@ selectAction rm = do L.find ((==) n . (+ 9) . keyIndex) $ hotkeys rm -maybeOption :: String -> Maybe String -> [String] +maybeOption :: T.Text -> Maybe T.Text -> [T.Text] maybeOption switch = maybe [] (\o -> [switch, o]) -dmenuArgs :: [String] +dmenuArgs :: [T.Text] dmenuArgs = ["-dmenu"] readRofi :: RofiConf c - => [String] - -> String - -> RofiIO c (Either (Int, String, String) String) + => [T.Text] + -> T.Text + -> RofiIO c (Either (Int, T.Text, T.Text) T.Text) readRofi uargs input = do dargs <- asks defArgs io $ readCmdEither "rofi" (dmenuArgs ++ dargs ++ uargs) input -readCmdSuccess :: String -> [String] -> String -> IO (Maybe String) +readCmdSuccess :: T.Text -> [T.Text] -> T.Text -> IO (Maybe T.Text) readCmdSuccess cmd args input = either (const Nothing) Just <$> readCmdEither cmd args input readCmdEither - :: String - -> [String] - -> String - -> IO (Either (Int, String, String) String) + :: T.Text + -> [T.Text] + -> T.Text + -> IO (Either (Int, T.Text, T.Text) T.Text) readCmdEither cmd args input = resultToEither - <$> readProcessWithExitCode cmd args input + <$> readProcessWithExitCode (T.unpack cmd) (fmap T.unpack args) (T.unpack input) readCmdEither' - :: String - -> [String] - -> String - -> [(String, String)] - -> IO (Either (Int, String, String) String) + :: T.Text + -> [T.Text] + -> T.Text + -> [(T.Text, T.Text)] + -> IO (Either (Int, T.Text, T.Text) T.Text) readCmdEither' cmd args input environ = resultToEither - <$> readCreateProcessWithExitCode p input + <$> readCreateProcessWithExitCode p (T.unpack input) where - p = (proc cmd args) {env = Just environ} + p = + (proc (T.unpack cmd) (fmap T.unpack args)) + { env = Just $ fmap (bimap T.unpack T.unpack) environ + } resultToEither :: (ExitCode, String, String) - -> Either (Int, String, String) String -resultToEither (ExitSuccess, out, _) = Right $ stripWS out -resultToEither (ExitFailure n, out, err) = Left (n, stripWS out, stripWS err) + -> Either (Int, T.Text, T.Text) T.Text +resultToEither (ExitSuccess, out, _) = Right $ stripWS $ T.pack out +resultToEither (ExitFailure n, out, err) = + Left (n, stripWS $ T.pack out, stripWS $ T.pack err) -stripWS :: String -> String -stripWS = reverse . dropWhile isSpace . reverse +stripWS :: T.Text -> T.Text +stripWS = T.reverse . T.dropWhile isSpace . T.reverse -joinNewline :: [String] -> String -joinNewline = L.intercalate "\n" +joinNewline :: [T.Text] -> T.Text +joinNewline = T.intercalate "\n" -readPassword :: IO (Maybe String) +readPassword :: IO (Maybe T.Text) readPassword = readPassword' "Password" -readPassword' :: String -> IO (Maybe String) +readPassword' :: T.Text -> IO (Maybe T.Text) readPassword' p = readCmdSuccess "rofi" args "" where args = dmenuArgs ++ ["-p", p, "-password"]