Compare commits
No commits in common. "cfe0607e2ec83e05074477e34b89bd047f49e296" and "3e9b08db086a3da3471c0096e1f4949d4dfe7cd0" have entirely different histories.
cfe0607e2e
...
3e9b08db08
|
@ -0,0 +1,357 @@
|
||||||
|
# stylish-haskell configuration file
|
||||||
|
# ==================================
|
||||||
|
|
||||||
|
# The stylish-haskell tool is mainly configured by specifying steps. These steps
|
||||||
|
# are a list, so they have an order, and one specific step may appear more than
|
||||||
|
# once (if needed). Each file is processed by these steps in the given order.
|
||||||
|
steps:
|
||||||
|
# Convert some ASCII sequences to their Unicode equivalents. This is disabled
|
||||||
|
# by default.
|
||||||
|
# - unicode_syntax:
|
||||||
|
# # In order to make this work, we also need to insert the UnicodeSyntax
|
||||||
|
# # language pragma. If this flag is set to true, we insert it when it's
|
||||||
|
# # not already present. You may want to disable it if you configure
|
||||||
|
# # language extensions using some other method than pragmas. Default:
|
||||||
|
# # true.
|
||||||
|
# add_language_pragma: true
|
||||||
|
|
||||||
|
# Format module header
|
||||||
|
#
|
||||||
|
# Currently, this option is not configurable and will format all exports and
|
||||||
|
# module declarations to minimize diffs
|
||||||
|
#
|
||||||
|
# - module_header:
|
||||||
|
# # How many spaces use for indentation in the module header.
|
||||||
|
# indent: 4
|
||||||
|
#
|
||||||
|
# # Should export lists be sorted? Sorting is only performed within the
|
||||||
|
# # export section, as delineated by Haddock comments.
|
||||||
|
# sort: true
|
||||||
|
#
|
||||||
|
# # See `separate_lists` for the `imports` step.
|
||||||
|
# separate_lists: true
|
||||||
|
|
||||||
|
# Format record definitions. This is disabled by default.
|
||||||
|
#
|
||||||
|
# You can control the layout of record fields. The only rules that can't be configured
|
||||||
|
# are these:
|
||||||
|
#
|
||||||
|
# - "|" is always aligned with "="
|
||||||
|
# - "," in fields is always aligned with "{"
|
||||||
|
# - "}" is likewise always aligned with "{"
|
||||||
|
#
|
||||||
|
# - records:
|
||||||
|
# # How to format equals sign between type constructor and data constructor.
|
||||||
|
# # Possible values:
|
||||||
|
# # - "same_line" -- leave "=" AND data constructor on the same line as the type constructor.
|
||||||
|
# # - "indent N" -- insert a new line and N spaces from the beginning of the next line.
|
||||||
|
# equals: "indent 2"
|
||||||
|
#
|
||||||
|
# # How to format first field of each record constructor.
|
||||||
|
# # Possible values:
|
||||||
|
# # - "same_line" -- "{" and first field goes on the same line as the data constructor.
|
||||||
|
# # - "indent N" -- insert a new line and N spaces from the beginning of the data constructor
|
||||||
|
# first_field: "indent 2"
|
||||||
|
#
|
||||||
|
# # How many spaces to insert between the column with "," and the beginning of the comment in the next line.
|
||||||
|
# field_comment: 2
|
||||||
|
#
|
||||||
|
# # How many spaces to insert before "deriving" clause. Deriving clauses are always on separate lines.
|
||||||
|
# deriving: 2
|
||||||
|
#
|
||||||
|
# # How many spaces to insert before "via" clause counted from indentation of deriving clause
|
||||||
|
# # Possible values:
|
||||||
|
# # - "same_line" -- "via" part goes on the same line as "deriving" keyword.
|
||||||
|
# # - "indent N" -- insert a new line and N spaces from the beginning of "deriving" keyword.
|
||||||
|
# via: "indent 2"
|
||||||
|
#
|
||||||
|
# # Sort typeclass names in the "deriving" list alphabetically.
|
||||||
|
# sort_deriving: true
|
||||||
|
#
|
||||||
|
# # Wheter or not to break enums onto several lines
|
||||||
|
# #
|
||||||
|
# # Default: false
|
||||||
|
# break_enums: false
|
||||||
|
#
|
||||||
|
# # Whether or not to break single constructor data types before `=` sign
|
||||||
|
# #
|
||||||
|
# # Default: true
|
||||||
|
# break_single_constructors: true
|
||||||
|
#
|
||||||
|
# # Whether or not to curry constraints on function.
|
||||||
|
# #
|
||||||
|
# # E.g: @allValues :: Enum a => Bounded a => Proxy a -> [a]@
|
||||||
|
# #
|
||||||
|
# # Instead of @allValues :: (Enum a, Bounded a) => Proxy a -> [a]@
|
||||||
|
# #
|
||||||
|
# # Default: false
|
||||||
|
# curried_context: false
|
||||||
|
|
||||||
|
# Align the right hand side of some elements. This is quite conservative
|
||||||
|
# and only applies to statements where each element occupies a single
|
||||||
|
# line.
|
||||||
|
# Possible values:
|
||||||
|
# - always - Always align statements.
|
||||||
|
# - adjacent - Align statements that are on adjacent lines in groups.
|
||||||
|
# - never - Never align statements.
|
||||||
|
# All default to always.
|
||||||
|
- simple_align:
|
||||||
|
cases: always
|
||||||
|
top_level_patterns: always
|
||||||
|
records: always
|
||||||
|
multi_way_if: always
|
||||||
|
|
||||||
|
# Import cleanup
|
||||||
|
- imports:
|
||||||
|
# There are different ways we can align names and lists.
|
||||||
|
#
|
||||||
|
# - global: Align the import names and import list throughout the entire
|
||||||
|
# file.
|
||||||
|
#
|
||||||
|
# - file: Like global, but don't add padding when there are no qualified
|
||||||
|
# imports in the file.
|
||||||
|
#
|
||||||
|
# - group: Only align the imports per group (a group is formed by adjacent
|
||||||
|
# import lines).
|
||||||
|
#
|
||||||
|
# - none: Do not perform any alignment.
|
||||||
|
#
|
||||||
|
# Default: global.
|
||||||
|
align: global
|
||||||
|
|
||||||
|
# The following options affect only import list alignment.
|
||||||
|
#
|
||||||
|
# List align has following options:
|
||||||
|
#
|
||||||
|
# - after_alias: Import list is aligned with end of import including
|
||||||
|
# 'as' and 'hiding' keywords.
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr, head,
|
||||||
|
# > init, last, length)
|
||||||
|
#
|
||||||
|
# - with_alias: Import list is aligned with start of alias or hiding.
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr, head,
|
||||||
|
# > init, last, length)
|
||||||
|
#
|
||||||
|
# - with_module_name: Import list is aligned `list_padding` spaces after
|
||||||
|
# the module name.
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr, head,
|
||||||
|
# init, last, length)
|
||||||
|
#
|
||||||
|
# This is mainly intended for use with `pad_module_names: false`.
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr, head,
|
||||||
|
# init, last, length, scanl, scanr, take, drop,
|
||||||
|
# sort, nub)
|
||||||
|
#
|
||||||
|
# - new_line: Import list starts always on new line.
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List
|
||||||
|
# > (concat, foldl, foldr, head, init, last, length)
|
||||||
|
#
|
||||||
|
# - repeat: Repeat the module name to align the import list.
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr, head)
|
||||||
|
# > import qualified Data.List as List (init, last, length)
|
||||||
|
#
|
||||||
|
# Default: after_alias
|
||||||
|
list_align: after_alias
|
||||||
|
|
||||||
|
# Right-pad the module names to align imports in a group:
|
||||||
|
#
|
||||||
|
# - true: a little more readable
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr,
|
||||||
|
# > init, last, length)
|
||||||
|
# > import qualified Data.List.Extra as List (concat, foldl, foldr,
|
||||||
|
# > init, last, length)
|
||||||
|
#
|
||||||
|
# - false: diff-safe
|
||||||
|
#
|
||||||
|
# > import qualified Data.List as List (concat, foldl, foldr, init,
|
||||||
|
# > last, length)
|
||||||
|
# > import qualified Data.List.Extra as List (concat, foldl, foldr,
|
||||||
|
# > init, last, length)
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
pad_module_names: true
|
||||||
|
|
||||||
|
# Long list align style takes effect when import is too long. This is
|
||||||
|
# determined by 'columns' setting.
|
||||||
|
#
|
||||||
|
# - inline: This option will put as much specs on same line as possible.
|
||||||
|
#
|
||||||
|
# - new_line: Import list will start on new line.
|
||||||
|
#
|
||||||
|
# - new_line_multiline: Import list will start on new line when it's
|
||||||
|
# short enough to fit to single line. Otherwise it'll be multiline.
|
||||||
|
#
|
||||||
|
# - multiline: One line per import list entry.
|
||||||
|
# Type with constructor list acts like single import.
|
||||||
|
#
|
||||||
|
# > import qualified Data.Map as M
|
||||||
|
# > ( empty
|
||||||
|
# > , singleton
|
||||||
|
# > , ...
|
||||||
|
# > , delete
|
||||||
|
# > )
|
||||||
|
#
|
||||||
|
# Default: inline
|
||||||
|
long_list_align: multiline
|
||||||
|
|
||||||
|
# Align empty list (importing instances)
|
||||||
|
#
|
||||||
|
# Empty list align has following options
|
||||||
|
#
|
||||||
|
# - inherit: inherit list_align setting
|
||||||
|
#
|
||||||
|
# - right_after: () is right after the module name:
|
||||||
|
#
|
||||||
|
# > import Vector.Instances ()
|
||||||
|
#
|
||||||
|
# Default: inherit
|
||||||
|
empty_list_align: inherit
|
||||||
|
|
||||||
|
# List padding determines indentation of import list on lines after import.
|
||||||
|
# This option affects 'long_list_align'.
|
||||||
|
#
|
||||||
|
# - <integer>: constant value
|
||||||
|
#
|
||||||
|
# - module_name: align under start of module name.
|
||||||
|
# Useful for 'file' and 'group' align settings.
|
||||||
|
#
|
||||||
|
# Default: 4
|
||||||
|
list_padding: 4
|
||||||
|
|
||||||
|
# Separate lists option affects formatting of import list for type
|
||||||
|
# or class. The only difference is single space between type and list
|
||||||
|
# of constructors, selectors and class functions.
|
||||||
|
#
|
||||||
|
# - true: There is single space between Foldable type and list of it's
|
||||||
|
# functions.
|
||||||
|
#
|
||||||
|
# > import Data.Foldable (Foldable (fold, foldl, foldMap))
|
||||||
|
#
|
||||||
|
# - false: There is no space between Foldable type and list of it's
|
||||||
|
# functions.
|
||||||
|
#
|
||||||
|
# > import Data.Foldable (Foldable(fold, foldl, foldMap))
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
separate_lists: true
|
||||||
|
|
||||||
|
# Space surround option affects formatting of import lists on a single
|
||||||
|
# line. The only difference is single space after the initial
|
||||||
|
# parenthesis and a single space before the terminal parenthesis.
|
||||||
|
#
|
||||||
|
# - true: There is single space associated with the enclosing
|
||||||
|
# parenthesis.
|
||||||
|
#
|
||||||
|
# > import Data.Foo ( foo )
|
||||||
|
#
|
||||||
|
# - false: There is no space associated with the enclosing parenthesis
|
||||||
|
#
|
||||||
|
# > import Data.Foo (foo)
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
space_surround: false
|
||||||
|
|
||||||
|
# Enabling this argument will use the new GHC lib parse to format imports.
|
||||||
|
#
|
||||||
|
# This currently assumes a few things, it will assume that you want post
|
||||||
|
# qualified imports. It is also not as feature complete as the old
|
||||||
|
# imports formatting.
|
||||||
|
#
|
||||||
|
# It does not remove redundant lines or merge lines. As such, the full
|
||||||
|
# feature scope is still pending.
|
||||||
|
#
|
||||||
|
# It _is_ however, a fine alternative if you are using features that are
|
||||||
|
# not parseable by haskell src extensions and you're comfortable with the
|
||||||
|
# presets.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
ghc_lib_parser: false
|
||||||
|
|
||||||
|
# Language pragmas
|
||||||
|
- language_pragmas:
|
||||||
|
# We can generate different styles of language pragma lists.
|
||||||
|
#
|
||||||
|
# - vertical: Vertical-spaced language pragmas, one per line.
|
||||||
|
#
|
||||||
|
# - compact: A more compact style.
|
||||||
|
#
|
||||||
|
# - compact_line: Similar to compact, but wrap each line with
|
||||||
|
# `{-#LANGUAGE #-}'.
|
||||||
|
#
|
||||||
|
# Default: vertical.
|
||||||
|
style: vertical
|
||||||
|
|
||||||
|
# Align affects alignment of closing pragma brackets.
|
||||||
|
#
|
||||||
|
# - true: Brackets are aligned in same column.
|
||||||
|
#
|
||||||
|
# - false: Brackets are not aligned together. There is only one space
|
||||||
|
# between actual import and closing bracket.
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
align: true
|
||||||
|
|
||||||
|
# stylish-haskell can detect redundancy of some language pragmas. If this
|
||||||
|
# is set to true, it will remove those redundant pragmas. Default: true.
|
||||||
|
remove_redundant: true
|
||||||
|
|
||||||
|
# Language prefix to be used for pragma declaration, this allows you to
|
||||||
|
# use other options non case-sensitive like "language" or "Language".
|
||||||
|
# If a non correct String is provided, it will default to: LANGUAGE.
|
||||||
|
language_prefix: LANGUAGE
|
||||||
|
|
||||||
|
# Replace tabs by spaces. This is disabled by default.
|
||||||
|
# - tabs:
|
||||||
|
# # Number of spaces to use for each tab. Default: 8, as specified by the
|
||||||
|
# # Haskell report.
|
||||||
|
# spaces: 8
|
||||||
|
|
||||||
|
# Remove trailing whitespace
|
||||||
|
- trailing_whitespace: {}
|
||||||
|
|
||||||
|
# Squash multiple spaces between the left and right hand sides of some
|
||||||
|
# elements into single spaces. Basically, this undoes the effect of
|
||||||
|
# simple_align but is a bit less conservative.
|
||||||
|
# - squash: {}
|
||||||
|
|
||||||
|
# A common setting is the number of columns (parts of) code will be wrapped
|
||||||
|
# to. Different steps take this into account.
|
||||||
|
#
|
||||||
|
# Set this to null to disable all line wrapping.
|
||||||
|
#
|
||||||
|
# Default: 80.
|
||||||
|
columns: 80
|
||||||
|
|
||||||
|
# By default, line endings are converted according to the OS. You can override
|
||||||
|
# preferred format here.
|
||||||
|
#
|
||||||
|
# - native: Native newline format. CRLF on Windows, LF on other OSes.
|
||||||
|
#
|
||||||
|
# - lf: Convert to LF ("\n").
|
||||||
|
#
|
||||||
|
# - crlf: Convert to CRLF ("\r\n").
|
||||||
|
#
|
||||||
|
# Default: native.
|
||||||
|
newline: native
|
||||||
|
|
||||||
|
# Sometimes, language extensions are specified in a cabal file or from the
|
||||||
|
# command line instead of using language pragmas in the file. stylish-haskell
|
||||||
|
# needs to be aware of these, so it can parse the file correctly.
|
||||||
|
#
|
||||||
|
# No language extensions are enabled by default.
|
||||||
|
# language_extensions:
|
||||||
|
# - TemplateHaskell
|
||||||
|
# - QuasiQuotes
|
||||||
|
|
||||||
|
# Attempt to find the cabal file in ancestors of the current directory, and
|
||||||
|
# parse options (currently only language extensions) from that.
|
||||||
|
#
|
||||||
|
# Default: true
|
||||||
|
cabal: true
|
250
app/rofi-dev.hs
250
app/rofi-dev.hs
|
@ -5,7 +5,7 @@
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- rofi-dev - a rofi prompt for mountable devices
|
-- | rofi-dev - a rofi prompt for mountable devices
|
||||||
--
|
--
|
||||||
-- Like all "mount helpers" this is basically a wrapper for low-level utilities
|
-- Like all "mount helpers" this is basically a wrapper for low-level utilities
|
||||||
-- the mount things from the command line. It also creates/destroys mountpoint
|
-- the mount things from the command line. It also creates/destroys mountpoint
|
||||||
|
@ -14,9 +14,11 @@
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
import Bitwarden.Internal
|
import Bitwarden.Internal
|
||||||
|
|
||||||
import Control.Lens
|
import Control.Lens
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.List.Split (splitOn)
|
import Data.List.Split (splitOn)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
@ -24,16 +26,21 @@ import Data.Maybe
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
|
|
||||||
import Dhall hiding (maybe, sequence, void)
|
import Dhall hiding (maybe, sequence, void)
|
||||||
import qualified Dhall.Map as DM
|
import qualified Dhall.Map as DM
|
||||||
|
|
||||||
import Rofi.Command
|
import Rofi.Command
|
||||||
|
|
||||||
|
import Text.Printf
|
||||||
|
|
||||||
import System.Console.GetOpt
|
import System.Console.GetOpt
|
||||||
import System.Directory
|
import System.Directory
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import System.FilePath.Posix
|
import System.FilePath.Posix
|
||||||
import System.Posix.User (getEffectiveUserName)
|
import System.Posix.User (getEffectiveUserName)
|
||||||
import System.Process
|
import System.Process
|
||||||
import Text.Printf
|
|
||||||
import UnliftIO.Exception
|
import UnliftIO.Exception
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
|
@ -45,8 +52,7 @@ parse args = case getOpt Permute options args of
|
||||||
(_, _, errs) -> ioError $ userError $ concat errs ++ usageInfo h options
|
(_, _, errs) -> ioError $ userError $ concat errs ++ usageInfo h options
|
||||||
where
|
where
|
||||||
h = "Usage: rofi-dev [OPTIONS] [-- ROFI-OPTIONS]"
|
h = "Usage: rofi-dev [OPTIONS] [-- ROFI-OPTIONS]"
|
||||||
defaultOpts r =
|
defaultOpts r = Opts
|
||||||
Opts
|
|
||||||
{ optsConfig = Nothing
|
{ optsConfig = Nothing
|
||||||
, optsAlias = Nothing
|
, optsAlias = Nothing
|
||||||
, optsUnmount = False
|
, optsUnmount = False
|
||||||
|
@ -55,20 +61,13 @@ parse args = case getOpt Permute options args of
|
||||||
|
|
||||||
options :: [OptDescr (Opts -> Opts)]
|
options :: [OptDescr (Opts -> Opts)]
|
||||||
options =
|
options =
|
||||||
[ Option
|
[ Option ['c'] ["config"]
|
||||||
['c']
|
|
||||||
["config"]
|
|
||||||
(ReqArg (\s m -> m { optsConfig = Just s } ) "CONF")
|
(ReqArg (\s m -> m { optsConfig = Just s } ) "CONF")
|
||||||
"The path to the config file"
|
"The path to the config file"
|
||||||
, Option
|
, Option ['m'] ["mount"]
|
||||||
['m']
|
|
||||||
["mount"]
|
|
||||||
(ReqArg (\s m -> m { optsAlias = Just s } ) "ALIAS")
|
(ReqArg (\s m -> m { optsAlias = Just s } ) "ALIAS")
|
||||||
"Mount the device specified by ALIAS directly"
|
"Mount the device specified by ALIAS directly"
|
||||||
, Option
|
, Option ['u'] ["unmount"] (NoArg (\m -> m { optsUnmount = True } ))
|
||||||
['u']
|
|
||||||
["unmount"]
|
|
||||||
(NoArg (\m -> m {optsUnmount = True}))
|
|
||||||
"Unmount the device specified by ALIAS instead of mounting it."
|
"Unmount the device specified by ALIAS instead of mounting it."
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -77,11 +76,10 @@ data Opts = Opts
|
||||||
, optsAlias :: Maybe String
|
, optsAlias :: Maybe String
|
||||||
, optsUnmount :: Bool
|
, optsUnmount :: Bool
|
||||||
, optsRofiArgs :: [String]
|
, optsRofiArgs :: [String]
|
||||||
}
|
} deriving Show
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Main prompt
|
-- | Main prompt
|
||||||
--
|
--
|
||||||
-- This command will have one Rofi prompt and will display all available
|
-- This command will have one Rofi prompt and will display all available
|
||||||
-- mounts grouped by device type (eg removable, sshfs, cifs, etc). I like
|
-- mounts grouped by device type (eg removable, sshfs, cifs, etc). I like
|
||||||
|
@ -94,8 +92,7 @@ runMounts opts = do
|
||||||
let tmpPath = fromMaybe defaultTmpPath $ scTmpPath =<< static
|
let tmpPath = fromMaybe defaultTmpPath $ scTmpPath =<< static
|
||||||
let staticDevs = maybe M.empty scDevices static
|
let staticDevs = maybe M.empty scDevices static
|
||||||
let verbose = fromMaybe False $ scVerbose =<< static
|
let verbose = fromMaybe False $ scVerbose =<< static
|
||||||
let mountconf =
|
let mountconf = MountConf
|
||||||
MountConf
|
|
||||||
{ mountconfVolatilePath = tmpPath
|
{ mountconfVolatilePath = tmpPath
|
||||||
, mountconfRofiArgs = optsRofiArgs opts
|
, mountconfRofiArgs = optsRofiArgs opts
|
||||||
, mountconfStaticDevs = staticDevs
|
, mountconfStaticDevs = staticDevs
|
||||||
|
@ -113,9 +110,7 @@ parseStaticConfig p = do
|
||||||
Right c -> return $ Just (c :: StaticConfig)
|
Right c -> return $ Just (c :: StaticConfig)
|
||||||
where
|
where
|
||||||
es = over substitutions (DM.union vars) defaultEvaluateSettings
|
es = over substitutions (DM.union vars) defaultEvaluateSettings
|
||||||
vars =
|
vars = DM.fromList $ catMaybes
|
||||||
DM.fromList $
|
|
||||||
catMaybes
|
|
||||||
[ toVar (auto :: Decoder TreeConfig)
|
[ toVar (auto :: Decoder TreeConfig)
|
||||||
, toVar (auto :: Decoder DeviceConfig)
|
, toVar (auto :: Decoder DeviceConfig)
|
||||||
, toVar (auto :: Decoder DataConfig)
|
, toVar (auto :: Decoder DataConfig)
|
||||||
|
@ -129,17 +124,11 @@ parseStaticConfig p = do
|
||||||
, toVar (auto :: Decoder BitwardenConfig)
|
, toVar (auto :: Decoder BitwardenConfig)
|
||||||
, toVar (auto :: Decoder MountConfig)
|
, toVar (auto :: Decoder MountConfig)
|
||||||
]
|
]
|
||||||
toVar a =
|
toVar a = fmap (\n -> (T.pack $ show n, maximum $ expected a))
|
||||||
fmap (\n -> (T.pack $ show n, maximum $ expected a)) $
|
$ listToMaybe $ snd $ splitTyConApp $ typeOf a
|
||||||
listToMaybe $
|
|
||||||
snd $
|
|
||||||
splitTyConApp $
|
|
||||||
typeOf a
|
|
||||||
|
|
||||||
runPrompt :: RofiConf c => [RofiGroup c] -> RofiIO c ()
|
runPrompt :: RofiConf c => [RofiGroup c] -> RofiIO c ()
|
||||||
runPrompt gs =
|
runPrompt gs = selectAction $ emptyMenu
|
||||||
selectAction $
|
|
||||||
emptyMenu
|
|
||||||
{ groups = gs
|
{ groups = gs
|
||||||
, prompt = Just "Select Device"
|
, prompt = Just "Select Device"
|
||||||
}
|
}
|
||||||
|
@ -147,26 +136,10 @@ runPrompt gs =
|
||||||
getGroups :: RofiMountIO [RofiGroup MountConf]
|
getGroups :: RofiMountIO [RofiGroup MountConf]
|
||||||
getGroups = do
|
getGroups = do
|
||||||
actions <- sequence [getStaticActions, getRemovableActions, getMTPActions]
|
actions <- sequence [getStaticActions, getRemovableActions, getMTPActions]
|
||||||
return $
|
return $ mapMaybe mkGroup
|
||||||
(++ [metaActions]) $
|
$ groupBy (\(hx, _) (hy, _) -> hx == hy)
|
||||||
mapMaybe mkGroup $
|
$ sortBy (\(hx, _) (hy, _) -> compare hx hy)
|
||||||
groupBy (\(hx, _) (hy, _) -> hx == hy) $
|
$ concat actions
|
||||||
sortBy (\(hx, _) (hy, _) -> compare hx hy) $
|
|
||||||
concat actions
|
|
||||||
where
|
|
||||||
metaActions =
|
|
||||||
titledGroup "Meta Actions" $
|
|
||||||
toRofiActions [(" Dismount All", dismountAll)]
|
|
||||||
|
|
||||||
dismountAll :: RofiMountIO ()
|
|
||||||
dismountAll = do
|
|
||||||
umount =<< asks (configToTree' . mountconfStaticDevs)
|
|
||||||
umount =<< getRemovableDevices
|
|
||||||
umount =<< getMTPDevices
|
|
||||||
return ()
|
|
||||||
where
|
|
||||||
umount :: Mountable a => [a] -> RofiMountIO ()
|
|
||||||
umount = mapM_ (`mountMaybe` True)
|
|
||||||
|
|
||||||
mountByAlias :: Bool -> String -> RofiMountIO ()
|
mountByAlias :: Bool -> String -> RofiMountIO ()
|
||||||
mountByAlias unmountFlag alias = do
|
mountByAlias unmountFlag alias = do
|
||||||
|
@ -175,9 +148,8 @@ mountByAlias unmountFlag alias = do
|
||||||
|
|
||||||
mkGroup :: [(Header, ProtoAction [String])] -> Maybe (RofiGroup MountConf)
|
mkGroup :: [(Header, ProtoAction [String])] -> Maybe (RofiGroup MountConf)
|
||||||
mkGroup [] = Nothing
|
mkGroup [] = Nothing
|
||||||
mkGroup as =
|
mkGroup as = let ((h, _):_) = as in
|
||||||
let ((h, _) : _) = as
|
Just $ titledGroup (show h) $ toRofiActions $ alignEntries $ fmap snd as
|
||||||
in Just $ titledGroup (show h) $ toRofiActions $ alignEntries $ fmap snd as
|
|
||||||
|
|
||||||
alignSep :: String
|
alignSep :: String
|
||||||
alignSep = " | "
|
alignSep = " | "
|
||||||
|
@ -186,8 +158,7 @@ alignEntries :: [ProtoAction [String]] -> [(String, RofiMountIO ())]
|
||||||
alignEntries ps = zip (align es) as
|
alignEntries ps = zip (align es) as
|
||||||
where
|
where
|
||||||
(es, as) = unzip $ fmap (\(ProtoAction e a) -> (e, a)) ps
|
(es, as) = unzip $ fmap (\(ProtoAction e a) -> (e, a)) ps
|
||||||
align =
|
align = fmap (intercalate alignSep)
|
||||||
fmap (intercalate alignSep)
|
|
||||||
. transpose
|
. transpose
|
||||||
. mapToLast pad
|
. mapToLast pad
|
||||||
. transpose
|
. transpose
|
||||||
|
@ -198,21 +169,20 @@ alignEntries ps = zip (align es) as
|
||||||
mapToLast f (x:xs) = f x : mapToLast f xs
|
mapToLast f (x:xs) = f x : mapToLast f xs
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Global config used in the reader monad stack
|
-- | Global config used in the reader monad stack
|
||||||
|
|
||||||
data MountConf = MountConf
|
data MountConf = MountConf
|
||||||
{ mountconfVolatilePath :: FilePath
|
{ mountconfVolatilePath :: FilePath
|
||||||
, mountconfRofiArgs :: [String]
|
, mountconfRofiArgs :: [String]
|
||||||
, mountconfStaticDevs :: M.Map String TreeConfig
|
, mountconfStaticDevs :: M.Map String TreeConfig
|
||||||
, mountconfVerbose :: Bool
|
, mountconfVerbose :: Bool
|
||||||
}
|
} deriving Show
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
instance RofiConf MountConf where
|
instance RofiConf MountConf where
|
||||||
defArgs MountConf { mountconfRofiArgs = a } = a
|
defArgs MountConf { mountconfRofiArgs = a } = a
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Mountable typeclass
|
-- | Mountable typeclass
|
||||||
--
|
--
|
||||||
-- Class to provide common interface for anything that can be mounted.
|
-- Class to provide common interface for anything that can be mounted.
|
||||||
|
|
||||||
|
@ -249,7 +219,7 @@ class Mountable a where
|
||||||
mountState :: a -> RofiMountIO MountState
|
mountState :: a -> RofiMountIO MountState
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Actionable typeclass
|
-- | Actionable typeclass
|
||||||
--
|
--
|
||||||
-- Class to provide common interface for anything that can be presented in the
|
-- Class to provide common interface for anything that can be presented in the
|
||||||
-- Rofi menu as an action. Note that this must be separate from the Mountable
|
-- Rofi menu as an action. Note that this must be separate from the Mountable
|
||||||
|
@ -282,17 +252,13 @@ class Mountable a => Actionable a where
|
||||||
mountedPrefix Mounted True = "* "
|
mountedPrefix Mounted True = "* "
|
||||||
mountedPrefix Partial True = "- "
|
mountedPrefix Partial True = "- "
|
||||||
|
|
||||||
mountableToAction
|
mountableToAction :: Actionable a => RofiMountIO [a] -> RofiMountIO [(Header, ProtoAction [String])]
|
||||||
:: Actionable a
|
|
||||||
=> RofiMountIO [a]
|
|
||||||
-> RofiMountIO [(Header, ProtoAction [String])]
|
|
||||||
mountableToAction ms = mapM mkAction =<< ms
|
mountableToAction ms = mapM mkAction =<< ms
|
||||||
|
|
||||||
type RofiMountIO a = RofiIO MountConf a
|
type RofiMountIO a = RofiIO MountConf a
|
||||||
|
|
||||||
-- headers appear in the order listed here (per Enum)
|
-- headers appear in the order listed here (per Enum)
|
||||||
data Header
|
data Header = CIFSHeader
|
||||||
= CIFSHeader
|
|
||||||
| SSHFSHeader
|
| SSHFSHeader
|
||||||
| VeracryptHeader
|
| VeracryptHeader
|
||||||
| RemovableHeader
|
| RemovableHeader
|
||||||
|
@ -315,18 +281,16 @@ instance Ord Header where
|
||||||
data ProtoAction a = ProtoAction a (RofiMountIO ())
|
data ProtoAction a = ProtoAction a (RofiMountIO ())
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Static device configuration (dhall)
|
-- | Static device configuration (dhall)
|
||||||
|
|
||||||
data MountConfig = MountConfig
|
data MountConfig = MountConfig
|
||||||
{ mpPath :: FilePath
|
{ mpPath :: FilePath
|
||||||
, mpLabel :: Maybe String
|
, mpLabel :: Maybe String
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data BitwardenConfig = BitwardenConfig
|
data BitwardenConfig = BitwardenConfig
|
||||||
{ bwKey :: String
|
{ bwKey :: String
|
||||||
, bwTries :: Integer
|
, bwTries :: Integer }
|
||||||
}
|
|
||||||
deriving (Show, Generic, FromDhall)
|
deriving (Show, Generic, FromDhall)
|
||||||
|
|
||||||
newtype SecretConfig = SecretConfig
|
newtype SecretConfig = SecretConfig
|
||||||
|
@ -337,8 +301,7 @@ newtype PromptConfig = PromptConfig
|
||||||
{ promptTries :: Integer }
|
{ promptTries :: Integer }
|
||||||
deriving (Show, Generic, FromDhall)
|
deriving (Show, Generic, FromDhall)
|
||||||
|
|
||||||
data PasswordConfig
|
data PasswordConfig = PwdBW BitwardenConfig
|
||||||
= PwdBW BitwardenConfig
|
|
||||||
| PwdLS SecretConfig
|
| PwdLS SecretConfig
|
||||||
| PwdPr PromptConfig
|
| PwdPr PromptConfig
|
||||||
deriving (Show, Generic, FromDhall)
|
deriving (Show, Generic, FromDhall)
|
||||||
|
@ -349,11 +312,9 @@ data CIFSOpts = CIFSOpts
|
||||||
, cifsoptsUID :: Maybe Integer
|
, cifsoptsUID :: Maybe Integer
|
||||||
, cifsoptsGID :: Maybe Integer
|
, cifsoptsGID :: Maybe Integer
|
||||||
, cifsoptsIocharset :: Maybe String
|
, cifsoptsIocharset :: Maybe String
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data DataConfig
|
data DataConfig = VeracryptConfig VeracryptData
|
||||||
= VeracryptConfig VeracryptData
|
|
||||||
| SSHFSConfig SSHFSData
|
| SSHFSConfig SSHFSData
|
||||||
| CIFSConfig CIFSData
|
| CIFSConfig CIFSData
|
||||||
deriving (Show, Generic, FromDhall)
|
deriving (Show, Generic, FromDhall)
|
||||||
|
@ -361,45 +322,39 @@ data DataConfig
|
||||||
data VeracryptData = VeracryptData
|
data VeracryptData = VeracryptData
|
||||||
{ vcVolume :: String
|
{ vcVolume :: String
|
||||||
, vcPassword :: Maybe PasswordConfig
|
, vcPassword :: Maybe PasswordConfig
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data SSHFSData = SSHFSData
|
data SSHFSData = SSHFSData
|
||||||
{ sshfsRemote :: String
|
{ sshfsRemote :: String
|
||||||
, sshfsPassword :: Maybe PasswordConfig
|
, sshfsPassword :: Maybe PasswordConfig
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data CIFSData = CIFSData
|
data CIFSData = CIFSData
|
||||||
{ cifsRemote :: String
|
{ cifsRemote :: String
|
||||||
, cifsSudo :: Bool
|
, cifsSudo :: Bool
|
||||||
, cifsPassword :: Maybe PasswordConfig
|
, cifsPassword :: Maybe PasswordConfig
|
||||||
, cifsOpts :: Maybe CIFSOpts
|
, cifsOpts :: Maybe CIFSOpts
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data DeviceConfig = DeviceConfig
|
data DeviceConfig = DeviceConfig
|
||||||
{ deviceMount :: MountConfig
|
{ deviceMount :: MountConfig
|
||||||
, deviceData :: DataConfig
|
, deviceData :: DataConfig
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data TreeConfig = TreeConfig
|
data TreeConfig = TreeConfig
|
||||||
{ tcParent :: DeviceConfig
|
{ tcParent :: DeviceConfig
|
||||||
, tcChildren :: V.Vector String
|
, tcChildren :: V.Vector String
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
data StaticConfig = StaticConfig
|
data StaticConfig = StaticConfig
|
||||||
{ scTmpPath :: Maybe String
|
{ scTmpPath :: Maybe String
|
||||||
, scVerbose :: Maybe Bool
|
, scVerbose :: Maybe Bool
|
||||||
, scDevices :: M.Map String TreeConfig
|
, scDevices :: M.Map String TreeConfig
|
||||||
}
|
} deriving (Show, Generic, FromDhall)
|
||||||
deriving (Show, Generic, FromDhall)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Static devices trees
|
-- | Static devices trees
|
||||||
|
--
|
||||||
-- Static devices as defined in the config file may declare dependencies on
|
-- Static devices as defined in the config file may declare dependencies on
|
||||||
-- other static devices, and thus are best represented as a tree. Note that the
|
-- other static devices, and thus are best represented as a tree. Note that the
|
||||||
-- tree is both Actionable and Mountable, where each node in the tree is only
|
-- tree is both Actionable and Mountable, where each node in the tree is only
|
||||||
|
@ -445,7 +400,7 @@ configToTree devMap TreeConfig {tcParent = p, tcChildren = c} =
|
||||||
go ds = configToTree devMap <$> mapMaybe (`M.lookup` devMap) ds
|
go ds = configToTree devMap <$> mapMaybe (`M.lookup` devMap) ds
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Static devices
|
-- | Static devices
|
||||||
--
|
--
|
||||||
-- This is complex because there may be multiple classes of static devices
|
-- This is complex because there may be multiple classes of static devices
|
||||||
-- in the config file, and each device may depend on another device that is
|
-- in the config file, and each device may depend on another device that is
|
||||||
|
@ -457,27 +412,24 @@ configToTree devMap TreeConfig {tcParent = p, tcChildren = c} =
|
||||||
instance Mountable DeviceConfig where
|
instance Mountable DeviceConfig where
|
||||||
mount DeviceConfig{ deviceMount = m, deviceData = devData} False = do
|
mount DeviceConfig{ deviceMount = m, deviceData = devData} False = do
|
||||||
m' <- getAbsMountpoint m
|
m' <- getAbsMountpoint m
|
||||||
withTmpMountDir m' $
|
withTmpMountDir m'
|
||||||
io $
|
$ io
|
||||||
case devData of
|
$ case devData of
|
||||||
SSHFSConfig (SSHFSData { sshfsRemote = r, sshfsPassword = p }) ->
|
SSHFSConfig (SSHFSData { sshfsRemote = r, sshfsPassword = p }) ->
|
||||||
mountSSHFS m' p r
|
mountSSHFS m' p r
|
||||||
CIFSConfig
|
CIFSConfig (CIFSData
|
||||||
( CIFSData
|
|
||||||
{ cifsRemote = r
|
{ cifsRemote = r
|
||||||
, cifsSudo = s
|
, cifsSudo = s
|
||||||
, cifsPassword = p
|
, cifsPassword = p
|
||||||
, cifsOpts = o
|
, cifsOpts = o
|
||||||
}
|
}) ->
|
||||||
) ->
|
|
||||||
mountCIFS s r m' o p
|
mountCIFS s r m' o p
|
||||||
VeracryptConfig
|
VeracryptConfig (VeracryptData
|
||||||
( VeracryptData
|
|
||||||
{ vcPassword = p
|
{ vcPassword = p
|
||||||
, vcVolume = v
|
, vcVolume = v
|
||||||
}
|
}) ->
|
||||||
) ->
|
|
||||||
mountVeracrypt m' p v
|
mountVeracrypt m' p v
|
||||||
|
|
||||||
mount DeviceConfig{ deviceMount = m, deviceData = d } True = do
|
mount DeviceConfig{ deviceMount = m, deviceData = d } True = do
|
||||||
m' <- getAbsMountpoint m
|
m' <- getAbsMountpoint m
|
||||||
runAndRemoveDir m' $ io $ case d of
|
runAndRemoveDir m' $ io $ case d of
|
||||||
|
@ -485,9 +437,7 @@ instance Mountable DeviceConfig where
|
||||||
VeracryptConfig _ -> runVeraCrypt ["-d", m'] ""
|
VeracryptConfig _ -> runVeraCrypt ["-d", m'] ""
|
||||||
_ -> runMount "umount" [m'] ""
|
_ -> runMount "umount" [m'] ""
|
||||||
|
|
||||||
allInstalled DeviceConfig {deviceData = devData} =
|
allInstalled DeviceConfig{ deviceData = devData } = io $ isJust
|
||||||
io $
|
|
||||||
isJust
|
|
||||||
<$> findExecutable (exe devData)
|
<$> findExecutable (exe devData)
|
||||||
where
|
where
|
||||||
exe SSHFSConfig{} = "sshfs"
|
exe SSHFSConfig{} = "sshfs"
|
||||||
|
@ -502,8 +452,7 @@ instance Mountable DeviceConfig where
|
||||||
b <- (io . isDirMounted) =<< getAbsMountpoint m
|
b <- (io . isDirMounted) =<< getAbsMountpoint m
|
||||||
return $ if b then Mounted else Unmounted
|
return $ if b then Mounted else Unmounted
|
||||||
|
|
||||||
getLabel
|
getLabel DeviceConfig
|
||||||
DeviceConfig
|
|
||||||
{ deviceMount = MountConfig { mpPath = p, mpLabel = l }
|
{ deviceMount = MountConfig { mpPath = p, mpLabel = l }
|
||||||
} = fromMaybe (takeFileName p) l
|
} = fromMaybe (takeFileName p) l
|
||||||
|
|
||||||
|
@ -513,13 +462,8 @@ mountSSHFS mountpoint pwdConfig remote =
|
||||||
where
|
where
|
||||||
run other = runMount "sshfs" (other ++ [remote, mountpoint])
|
run other = runMount "sshfs" (other ++ [remote, mountpoint])
|
||||||
|
|
||||||
mountCIFS
|
mountCIFS :: Bool -> String -> FilePath -> Maybe CIFSOpts
|
||||||
:: Bool
|
-> Maybe PasswordConfig -> IO MountResult
|
||||||
-> String
|
|
||||||
-> FilePath
|
|
||||||
-> Maybe CIFSOpts
|
|
||||||
-> Maybe PasswordConfig
|
|
||||||
-> IO MountResult
|
|
||||||
mountCIFS useSudo remote mountpoint opts pwdConfig =
|
mountCIFS useSudo remote mountpoint opts pwdConfig =
|
||||||
withPasswordGetter pwdConfig runPwd run
|
withPasswordGetter pwdConfig runPwd run
|
||||||
where
|
where
|
||||||
|
@ -530,8 +474,7 @@ mountCIFS useSudo remote mountpoint opts pwdConfig =
|
||||||
fromCIFSOpts :: CIFSOpts -> String
|
fromCIFSOpts :: CIFSOpts -> String
|
||||||
fromCIFSOpts o = intercalate "," $ mapMaybe concatMaybe fs
|
fromCIFSOpts o = intercalate "," $ mapMaybe concatMaybe fs
|
||||||
where
|
where
|
||||||
fs =
|
fs = [ ("username", cifsoptsUsername)
|
||||||
[ ("username", cifsoptsUsername)
|
|
||||||
, ("workgroup", cifsoptsWorkgroup)
|
, ("workgroup", cifsoptsWorkgroup)
|
||||||
, ("uid", fmap show . cifsoptsUID)
|
, ("uid", fmap show . cifsoptsUID)
|
||||||
, ("gid", fmap show . cifsoptsGID)
|
, ("gid", fmap show . cifsoptsGID)
|
||||||
|
@ -541,8 +484,8 @@ fromCIFSOpts o = intercalate "," $ mapMaybe concatMaybe fs
|
||||||
|
|
||||||
mountVeracrypt :: FilePath -> Maybe PasswordConfig -> String -> IO MountResult
|
mountVeracrypt :: FilePath -> Maybe PasswordConfig -> String -> IO MountResult
|
||||||
mountVeracrypt mountpoint pwdConfig volume =
|
mountVeracrypt mountpoint pwdConfig volume =
|
||||||
withPasswordGetter pwdConfig (runVeraCrypt (args ++ ["--stdin"])) $
|
withPasswordGetter pwdConfig (runVeraCrypt (args ++ ["--stdin"]))
|
||||||
runVeraCrypt args ""
|
$ runVeraCrypt args ""
|
||||||
where
|
where
|
||||||
args = [volume, mountpoint]
|
args = [volume, mountpoint]
|
||||||
|
|
||||||
|
@ -578,7 +521,7 @@ getStaticActions :: RofiMountIO [(Header, ProtoAction [String])]
|
||||||
getStaticActions = mountableToAction $ asks $ configToTree' . mountconfStaticDevs
|
getStaticActions = mountableToAction $ asks $ configToTree' . mountconfStaticDevs
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Password-getting functions for static devices
|
-- | Password-getting functions for static devices
|
||||||
|
|
||||||
type PasswordGetter = IO (Maybe String)
|
type PasswordGetter = IO (Maybe String)
|
||||||
|
|
||||||
|
@ -588,15 +531,14 @@ runSecret kvs = readCmdSuccess "secret-tool" ("lookup" : kvs') ""
|
||||||
kvs' = concatMap (\(k, v) -> [k, v]) $ M.toList kvs
|
kvs' = concatMap (\(k, v) -> [k, v]) $ M.toList kvs
|
||||||
|
|
||||||
runBitwarden :: String -> PasswordGetter
|
runBitwarden :: String -> PasswordGetter
|
||||||
runBitwarden pname =
|
runBitwarden pname = ((password . login) <=< find (\i -> name i == pname))
|
||||||
((password . login) <=< find (\i -> name i == pname))
|
|
||||||
<$> getItems
|
<$> getItems
|
||||||
|
|
||||||
runPromptLoop :: Integer -> PasswordGetter -> PasswordGetter
|
runPromptLoop :: Integer -> PasswordGetter -> PasswordGetter
|
||||||
runPromptLoop n pwd = do
|
runPromptLoop n pwd = do
|
||||||
res <- pwd
|
res <- pwd
|
||||||
if isNothing res
|
if isNothing res then
|
||||||
then if n <= 0 then return Nothing else runPromptLoop (n - 1) pwd
|
if n <= 0 then return Nothing else runPromptLoop (n-1) pwd
|
||||||
else return res
|
else return res
|
||||||
|
|
||||||
-- configToPwd :: PasswordConfig -> PasswordGetter
|
-- configToPwd :: PasswordConfig -> PasswordGetter
|
||||||
|
@ -620,18 +562,15 @@ configToPwd (PwdBW (BitwardenConfig {bwKey = k, bwTries = n})) =
|
||||||
configToPwd (PwdLS s) = runSecret $ secretAttributes s
|
configToPwd (PwdLS s) = runSecret $ secretAttributes s
|
||||||
configToPwd (PwdPr p) = flip runPromptLoop readPassword $ promptTries p
|
configToPwd (PwdPr p) = flip runPromptLoop readPassword $ promptTries p
|
||||||
|
|
||||||
withPasswordGetter
|
withPasswordGetter :: Maybe PasswordConfig -> (String -> IO MountResult)
|
||||||
:: Maybe PasswordConfig
|
-> IO MountResult -> IO MountResult
|
||||||
-> (String -> IO MountResult)
|
|
||||||
-> IO MountResult
|
|
||||||
-> IO MountResult
|
|
||||||
withPasswordGetter (Just pwdConfig) runPwd _ =
|
withPasswordGetter (Just pwdConfig) runPwd _ =
|
||||||
maybe (return $ MountError "Password could not be obtained") runPwd
|
maybe (return $ MountError "Password could not be obtained") runPwd
|
||||||
=<< configToPwd pwdConfig
|
=<< configToPwd pwdConfig
|
||||||
withPasswordGetter Nothing _ run = run
|
withPasswordGetter Nothing _ run = run
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Removable devices
|
-- | Removable devices
|
||||||
--
|
--
|
||||||
-- A device which can be removed (such as a flash drive). These are distinct
|
-- A device which can be removed (such as a flash drive). These are distinct
|
||||||
-- from any device in the static configuration in that they only have device
|
-- from any device in the static configuration in that they only have device
|
||||||
|
@ -668,8 +607,7 @@ instance Actionable Removable where
|
||||||
-- label shown on the prompt will be 'SIZE Volume' where size is the size of
|
-- label shown on the prompt will be 'SIZE Volume' where size is the size of
|
||||||
-- the device
|
-- the device
|
||||||
getRemovableDevices :: RofiConf c => RofiIO c [Removable]
|
getRemovableDevices :: RofiConf c => RofiIO c [Removable]
|
||||||
getRemovableDevices =
|
getRemovableDevices = fromLines toDev . lines
|
||||||
fromLines toDev . lines
|
|
||||||
<$> io (readProcess "lsblk" ["-n", "-r", "-o", columns] "")
|
<$> io (readProcess "lsblk" ["-n", "-r", "-o", columns] "")
|
||||||
where
|
where
|
||||||
columns = "FSTYPE,HOTPLUG,PATH,LABEL,SIZE"
|
columns = "FSTYPE,HOTPLUG,PATH,LABEL,SIZE"
|
||||||
|
@ -685,7 +623,7 @@ getRemovableActions :: RofiMountIO [(Header, ProtoAction [String])]
|
||||||
getRemovableActions = mountableToAction getRemovableDevices
|
getRemovableActions = mountableToAction getRemovableDevices
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- MTP devices
|
-- | MTP devices
|
||||||
|
|
||||||
mtpExe :: String
|
mtpExe :: String
|
||||||
mtpExe = "jmtpfs"
|
mtpExe = "jmtpfs"
|
||||||
|
@ -703,10 +641,11 @@ instance Mountable MTPFS where
|
||||||
-- TODO add autodismount to options
|
-- TODO add autodismount to options
|
||||||
let dev = "-device=" ++ b ++ "," ++ n
|
let dev = "-device=" ++ b ++ "," ++ n
|
||||||
withTmpMountDir m $ io $ runMount mtpExe [dev, m] ""
|
withTmpMountDir m $ io $ runMount mtpExe [dev, m] ""
|
||||||
|
|
||||||
mount MTPFS { mtpfsMountpoint = m } True =
|
mount MTPFS { mtpfsMountpoint = m } True =
|
||||||
runAndRemoveDir m $ io $ runMount "umount" [m] ""
|
runAndRemoveDir m $ io $ runMount "umount" [m] ""
|
||||||
|
|
||||||
-- \| return True always since the list won't even show without jmtpfs
|
-- | return True always since the list won't even show without jmtpfs
|
||||||
allInstalled _ = return True
|
allInstalled _ = return True
|
||||||
|
|
||||||
mountState MTPFS { mtpfsMountpoint = m } = do
|
mountState MTPFS { mtpfsMountpoint = m } = do
|
||||||
|
@ -725,16 +664,13 @@ getMTPDevices = do
|
||||||
dir <- asks mountconfVolatilePath
|
dir <- asks mountconfVolatilePath
|
||||||
res <- io $ readProcess mtpExe ["-l"] ""
|
res <- io $ readProcess mtpExe ["-l"] ""
|
||||||
return $ fromLines (toDev dir) $ toDevList res
|
return $ fromLines (toDev dir) $ toDevList res
|
||||||
toDevList =
|
toDevList = reverse
|
||||||
reverse
|
|
||||||
. takeWhile (not . isPrefixOf "Available devices")
|
. takeWhile (not . isPrefixOf "Available devices")
|
||||||
. reverse
|
. reverse
|
||||||
. lines
|
. lines
|
||||||
toDev dir s = case splitOn ", " s of
|
toDev dir s = case splitOn ", " s of
|
||||||
[busNum, devNum, _, _, desc, vendor] ->
|
[busNum, devNum, _, _, desc, vendor] -> let d = unwords [vendor, desc]
|
||||||
let d = unwords [vendor, desc]
|
in Just $ MTPFS
|
||||||
in Just $
|
|
||||||
MTPFS
|
|
||||||
{ mtpfsBus = busNum
|
{ mtpfsBus = busNum
|
||||||
, mtpfsDevice = devNum
|
, mtpfsDevice = devNum
|
||||||
, mtpfsMountpoint = dir </> canonicalize d
|
, mtpfsMountpoint = dir </> canonicalize d
|
||||||
|
@ -759,7 +695,7 @@ instance Actionable MTPFS where
|
||||||
groupHeader _ = MTPFSHeader
|
groupHeader _ = MTPFSHeader
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Notifications
|
-- | Notifications
|
||||||
|
|
||||||
data NotifyIcon = IconError | IconInfo
|
data NotifyIcon = IconError | IconInfo
|
||||||
|
|
||||||
|
@ -775,15 +711,13 @@ notifyMountResult mounted label result = case result of
|
||||||
verb = if mounted then "unmount" else "mount" :: String
|
verb = if mounted then "unmount" else "mount" :: String
|
||||||
|
|
||||||
notify :: NotifyIcon -> String -> Maybe String -> IO ()
|
notify :: NotifyIcon -> String -> Maybe String -> IO ()
|
||||||
notify icon summary body =
|
notify icon summary body = void $ spawnProcess "notify-send"
|
||||||
void $
|
$ maybe args (\b -> args ++ [b]) body
|
||||||
spawnProcess "notify-send" $
|
|
||||||
maybe args (\b -> args ++ [b]) body
|
|
||||||
where
|
where
|
||||||
args = ["-i", show icon, summary]
|
args = ["-i", show icon, summary]
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Mount commands
|
-- | Mount commands
|
||||||
|
|
||||||
data MountResult = MountSuccess | MountError String deriving (Show, Eq)
|
data MountResult = MountSuccess | MountError String deriving (Show, Eq)
|
||||||
|
|
||||||
|
@ -791,8 +725,7 @@ runMount :: String -> [String] -> String -> IO MountResult
|
||||||
runMount cmd args stdin = eitherToMountResult <$> readCmdEither cmd args stdin
|
runMount cmd args stdin = eitherToMountResult <$> readCmdEither cmd args stdin
|
||||||
|
|
||||||
runMount' :: String -> [String] -> String -> [(String, String)] -> IO MountResult
|
runMount' :: String -> [String] -> String -> [(String, String)] -> IO MountResult
|
||||||
runMount' cmd args stdin environ =
|
runMount' cmd args stdin environ = eitherToMountResult
|
||||||
eitherToMountResult
|
|
||||||
<$> readCmdEither' cmd args stdin environ
|
<$> readCmdEither' cmd args stdin environ
|
||||||
|
|
||||||
runMountSudoMaybe :: Bool -> String -> [String] -> IO MountResult
|
runMountSudoMaybe :: Bool -> String -> [String] -> IO MountResult
|
||||||
|
@ -800,8 +733,7 @@ runMountSudoMaybe useSudo cmd args =
|
||||||
runMountSudoMaybe' useSudo cmd args []
|
runMountSudoMaybe' useSudo cmd args []
|
||||||
|
|
||||||
runMountSudoMaybe' :: Bool -> String -> [String] -> [(String, String)] -> IO MountResult
|
runMountSudoMaybe' :: Bool -> String -> [String] -> [(String, String)] -> IO MountResult
|
||||||
runMountSudoMaybe' useSudo cmd args environ =
|
runMountSudoMaybe' useSudo cmd args environ = maybe
|
||||||
maybe
|
|
||||||
(runMount' cmd args "" environ)
|
(runMount' cmd args "" environ)
|
||||||
(\r -> runSudoMount' r cmd args environ)
|
(\r -> runSudoMount' r cmd args environ)
|
||||||
=<< if useSudo then readPassword' "Sudo Password" else return Nothing
|
=<< if useSudo then readPassword' "Sudo Password" else return Nothing
|
||||||
|
@ -821,7 +753,7 @@ eitherToMountResult (Right _) = MountSuccess
|
||||||
eitherToMountResult (Left (_, _, e)) = MountError e
|
eitherToMountResult (Left (_, _, e)) = MountError e
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Low-level mount functions
|
-- | Low-level mount functions
|
||||||
|
|
||||||
mountMap :: IO (M.Map FilePath String)
|
mountMap :: IO (M.Map FilePath String)
|
||||||
mountMap = do
|
mountMap = do
|
||||||
|
@ -857,8 +789,7 @@ rmDirOnMountError d f = do
|
||||||
-- | Run a mount command and create the mountpoint if it does not exist, and
|
-- | Run a mount command and create the mountpoint if it does not exist, and
|
||||||
-- remove the mountpoint if a mount error occurs
|
-- remove the mountpoint if a mount error occurs
|
||||||
withTmpMountDir :: FilePath -> RofiMountIO MountResult -> RofiMountIO MountResult
|
withTmpMountDir :: FilePath -> RofiMountIO MountResult -> RofiMountIO MountResult
|
||||||
withTmpMountDir m =
|
withTmpMountDir m = rmDirOnMountError m
|
||||||
rmDirOnMountError m
|
|
||||||
. bracketOnError_ (mkDirMaybe m) (rmDirMaybe m)
|
. bracketOnError_ (mkDirMaybe m) (rmDirMaybe m)
|
||||||
|
|
||||||
-- | Run an unmount command and remove the mountpoint if no errors occur
|
-- | Run an unmount command and remove the mountpoint if no errors occur
|
||||||
|
@ -872,10 +803,8 @@ mkDirMaybe :: FilePath -> RofiMountIO ()
|
||||||
mkDirMaybe fp = whenInMountDir fp $ io $ createDirectoryIfMissing True fp
|
mkDirMaybe fp = whenInMountDir fp $ io $ createDirectoryIfMissing True fp
|
||||||
|
|
||||||
rmDirMaybe :: FilePath -> RofiMountIO ()
|
rmDirMaybe :: FilePath -> RofiMountIO ()
|
||||||
rmDirMaybe fp =
|
rmDirMaybe fp = whenInMountDir fp $ unlessMountpoint fp
|
||||||
whenInMountDir fp $
|
$ asks mountconfVolatilePath >>= io . rmUntil fp
|
||||||
unlessMountpoint fp $
|
|
||||||
asks mountconfVolatilePath >>= io . rmUntil fp
|
|
||||||
where
|
where
|
||||||
rmUntil cur target = unless (target == cur) $ do
|
rmUntil cur target = unless (target == cur) $ do
|
||||||
removePathForcibly cur
|
removePathForcibly cur
|
||||||
|
@ -895,7 +824,7 @@ isDirMounted :: FilePath -> IO Bool
|
||||||
isDirMounted fp = elem fp <$> curMountpoints
|
isDirMounted fp = elem fp <$> curMountpoints
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Other functions
|
-- | Other functions
|
||||||
|
|
||||||
fromLines :: (String -> Maybe a) -> [String] -> [a]
|
fromLines :: (String -> Maybe a) -> [String] -> [a]
|
||||||
fromLines f = mapMaybe (f . stripWS)
|
fromLines f = mapMaybe (f . stripWS)
|
||||||
|
@ -905,8 +834,7 @@ splitBy :: Char -> String -> [String]
|
||||||
splitBy delimiter = foldr f [[]]
|
splitBy delimiter = foldr f [[]]
|
||||||
where
|
where
|
||||||
f _ [] = []
|
f _ [] = []
|
||||||
f c l@(x : xs)
|
f c l@(x:xs) | c == delimiter = []:l
|
||||||
| c == delimiter = [] : l
|
|
||||||
| otherwise = (c:x):xs
|
| otherwise = (c:x):xs
|
||||||
|
|
||||||
appendRoot :: FilePath -> FilePath -> FilePath
|
appendRoot :: FilePath -> FilePath -> FilePath
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
indentation: 2
|
|
||||||
function-arrows: leading
|
|
||||||
comma-style: leading
|
|
||||||
import-export-style: leading
|
|
||||||
indent-wheres: true
|
|
||||||
record-brace-space: true
|
|
||||||
newlines-between-decls: 1
|
|
||||||
haddock-style: single-line
|
|
||||||
haddock-style-module:
|
|
||||||
let-style: inline
|
|
||||||
in-style: right-align
|
|
||||||
respectful: false
|
|
||||||
fixities: []
|
|
||||||
unicode: never
|
|
Loading…
Reference in New Issue