aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/app/AddPath.hs
blob: 27ec0edcfdb435bb9753caeabd245cfb08c55e46 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
-- |
-- Copyright   : (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
-- License     : MIT
-- Maintainer  : Egor.Tensin@gmail.com
-- Stability   : experimental
-- Portability : Windows-only

module Main (main) where

import Control.Monad   (when, void)
import Control.Monad.Trans.Except (catchE, runExceptT, throwE)
import Data.List       (nub)
import System.IO.Error (ioError, isDoesNotExistError)

import Options.Applicative

import qualified WindowsEnv

import Utils.Path
import Utils.Prompt
import Utils.PromptMessage

data Options = Options
    { optName    :: WindowsEnv.Name
    , optYes     :: Bool
    , optGlobal  :: Bool
    , optPrepend :: Bool
    , optPaths   :: [String]
    } deriving (Eq, Show)

optionParser :: Parser Options
optionParser = Options
    <$> optNameDesc
    <*> optYesDesc
    <*> optGlobalDesc
    <*> optPrependDesc
    <*> optPathsDesc
  where
    optNameDesc = strOption
         $ long "name" <> short 'n'
        <> metavar "NAME" <> value "PATH"
        <> help "Variable name ('PATH' by default)"
    optYesDesc = switch
         $ long "yes" <> short 'y'
        <> help "Skip confirmation prompt"
    optGlobalDesc = switch
         $ long "global" <> short 'g'
        <> help "Add for all users"
    optPrependDesc = switch
         $ long "prepend" <> short 'p'
        <> help "Prepend to the variable (instead of appending)"
    optPathsDesc = many $ argument str
         $ metavar "PATH"
        <> help "Directories to add"

main :: IO ()
main = execParser parser >>= addPath
  where
    parser = info (helper <*> optionParser) $
        fullDesc <> progDesc "Add directories to your PATH"

addPath :: Options -> IO ()
addPath options = runExceptT doAddPath >>= either ioError return
  where
    varName = optName options
    pathsToAdd = nub $ optPaths options
    forAllUsers = optGlobal options
    skipPrompt = optYes options

    profile
        | forAllUsers = WindowsEnv.AllUsers
        | otherwise   = WindowsEnv.CurrentUser

    prepend = optPrepend options
    appendPaths old new
        | prepend = new ++ old
        | otherwise = old ++ new

    emptyIfMissing e
        | isDoesNotExistError e = return $ WindowsEnv.Value False ""
        | otherwise = throwE e

    doAddPath = do
        newPaths <- pathExpandAll pathsToAdd
        let newExpandable = pathAnyExpanded newPaths
        srcValue <- WindowsEnv.query profile varName `catchE` emptyIfMissing
        let srcExpandable = WindowsEnv.valueExpandable srcValue
        let destExpandable = newExpandable || srcExpandable
        srcPaths <- pathExpandValue srcValue
            { WindowsEnv.valueExpandable = destExpandable }
        let destPaths = appendPaths srcPaths $ filter (`notElem` srcPaths) newPaths
        let destPathsJoined = WindowsEnv.pathJoin $ map pathOriginal destPaths
        let destValue = WindowsEnv.Value destExpandable destPathsJoined
        when (srcValue /= destValue) $ promptAndEngrave srcValue destValue

    promptAndEngrave oldValue newValue = do
        let promptAnd = if skipPrompt
            then withoutPrompt
            else withPrompt $ oldNewMessage profile varName oldValue newValue
        let engrave = WindowsEnv.engrave profile varName newValue
        void $ promptAnd engrave