module Darcs.UI.Options.Util
( Flag
, DarcsOptDescr
, PrimDarcsOption
, noArg
, strArg
, optStrArg
, absPathArg
, absPathOrStdArg
, optAbsPathArg
, RawOptSpec(..)
, withDefault
, singleNoArg
, singleStrArg
, multiStrArg
, multiOptStrArg
, singleAbsPathArg
, multiAbsPathArg
, deprecated
, parseIntArg
, parseIndexRangeArg
, showIntArg
, showIndexRangeArg
, AbsolutePath
, AbsolutePathOrStd
, makeAbsolute
, makeAbsoluteOrStd
) where
import Darcs.Prelude
import Control.Exception ( Exception, throw )
import Data.Functor.Compose
import Data.List ( intercalate )
import Data.Maybe ( maybeToList, fromMaybe )
import Data.Typeable ( Typeable )
import System.Console.GetOpt ( OptDescr(..), ArgDescr(..) )
import Darcs.UI.Options.Core
import Darcs.UI.Options.Flags ( DarcsFlag )
import Darcs.UI.Options.Iso
import Darcs.Util.Path
( AbsolutePath
, AbsolutePathOrStd
, makeAbsolute
, makeAbsoluteOrStd
)
type Flag = DarcsFlag
type DarcsOptDescr = Compose OptDescr ((->) AbsolutePath)
type PrimDarcsOption v = forall a. PrimOptSpec DarcsOptDescr Flag a v
noArg :: [Char] -> [String] -> f -> String -> DarcsOptDescr f
noArg s l f h = Compose $ Option s l (NoArg (const f)) h
type SingleArgOptDescr a f =
[Char] -> [String] -> (a -> f) -> String -> String -> DarcsOptDescr f
strArg :: SingleArgOptDescr String f
strArg s l f a h = Compose $ Option s l (ReqArg (\x _ -> f x) a) h
optStrArg :: SingleArgOptDescr (Maybe String) f
optStrArg s l f a h = Compose $ Option s l (OptArg (\x _ -> f x) a) h
absPathArg :: SingleArgOptDescr AbsolutePath f
absPathArg s l f a h = Compose $ Option s l (ReqArg (\x wd -> f $ makeAbsolute wd x) a) h
absPathOrStdArg :: SingleArgOptDescr AbsolutePathOrStd f
absPathOrStdArg s l f a h = Compose $ Option s l (ReqArg (\x wd -> f $ makeAbsoluteOrStd wd x) a) h
optAbsPathArg :: [Char] -> [String] -> String -> (AbsolutePath -> f)
-> String -> String -> DarcsOptDescr f
optAbsPathArg s l d f a h = Compose $ Option s l (OptArg (\x wd -> f $ makeAbsolute wd $ fromMaybe d x) a) h
data RawOptSpec f v
= RawNoArg [Char] [String] f v String
| RawStrArg [Char] [String] (String -> f) (f -> [String]) (String -> v) (v -> [String])
String String
| RawAbsPathArg [Char] [String] (AbsolutePath -> f) (f -> [AbsolutePath])
(AbsolutePath -> v) (v -> [AbsolutePath]) String String
| RawAbsPathOrStdArg [Char] [String] (AbsolutePathOrStd -> f) (f -> [AbsolutePathOrStd])
(AbsolutePathOrStd -> v) (v -> [AbsolutePathOrStd]) String String
| RawOptAbsPathArg [Char] [String] (AbsolutePath -> f) (f -> [AbsolutePath])
(AbsolutePath -> v) (v -> [AbsolutePath]) String String String
instance IsoFunctor (RawOptSpec f) where
imap (Iso fw _) (RawNoArg s l f v h) = RawNoArg s l f (fw v) h
imap (Iso fw bw) (RawStrArg s l mkF unF mkV unV n h) = RawStrArg s l mkF unF (fw . mkV) (unV . bw) n h
imap (Iso fw bw) (RawAbsPathArg s l mkF unF mkV unV n h) = RawAbsPathArg s l mkF unF (fw . mkV) (unV . bw) n h
imap (Iso fw bw) (RawAbsPathOrStdArg s l mkF unF mkV unV n h) = RawAbsPathOrStdArg s l mkF unF (fw . mkV) (unV . bw) n h
imap (Iso fw bw) (RawOptAbsPathArg s l mkF unF mkV unV d n h) = RawOptAbsPathArg s l mkF unF (fw . mkV) (unV . bw) d n h
switchNames :: RawOptSpec f v -> [String]
switchNames (RawNoArg _ l _ _ _) = l
switchNames (RawStrArg _ l _ _ _ _ _ _) = l
switchNames (RawAbsPathArg _ l _ _ _ _ _ _) = l
switchNames (RawAbsPathOrStdArg _ l _ _ _ _ _ _) = l
switchNames (RawOptAbsPathArg _ l _ _ _ _ _ _ _) = l
rawUnparse :: Eq v => [RawOptSpec f v] -> v -> [f]
rawUnparse ropts val =
[ f | RawNoArg _ _ f v _ <- ropts, v == val ]
++ [ mkF s | RawStrArg _ _ mkF _ mkV unV _ _ <- ropts, s <- unV val, mkV s == val ]
++ [ mkF p | RawAbsPathArg _ _ mkF _ mkV unV _ _ <- ropts, p <- unV val, mkV p == val ]
++ [ mkF p | RawAbsPathOrStdArg _ _ mkF _ mkV unV _ _ <- ropts, p <- unV val, mkV p == val ]
++ [ mkF p | RawOptAbsPathArg _ _ mkF _ mkV unV _ _ _ <- ropts, p <- unV val, mkV p == val ]
rawParse :: Eq f => [RawOptSpec f v] -> [f] -> [(v,RawOptSpec f v)]
rawParse ropts = concatMap rawParseFlag where
rawParseFlag f = concatMap (go f) ropts
go f o@(RawNoArg _ _ f' v _) = [ (v, o) | f == f' ]
go f o@(RawStrArg _ _ _ unF mkV _ _ _) = [ (mkV s, o) | s <- unF f ]
go f o@(RawAbsPathArg _ _ _ unF mkV _ _ _) = [ (mkV p, o) | p <- unF f ]
go f o@(RawAbsPathOrStdArg _ _ _ unF mkV _ _ _) = [ (mkV p, o) | p <- unF f ]
go f o@(RawOptAbsPathArg _ _ _ unF mkV _ _ _ _) = [ (mkV p, o) | p <- unF f ]
defHead :: a -> [a] -> a
defHead def [] = def
defHead _ (x:_) = x
addDefaultHelp :: Eq v => v -> RawOptSpec f v -> DarcsOptDescr f
addDefaultHelp dval (RawNoArg s l f v h)
| dval == v = noArg s l f (h++" [DEFAULT]")
| otherwise = noArg s l f h
addDefaultHelp dval (RawStrArg s l mkF _ mkV unV a h)
| [dval] == map mkV (unV dval) = strArg s l mkF a (h++" [DEFAULT]")
| otherwise = strArg s l mkF a h
addDefaultHelp dval (RawAbsPathArg s l mkF _ mkV unV a h)
| [dval] == map mkV (unV dval) = absPathArg s l mkF a (h++" [DEFAULT]")
| otherwise = absPathArg s l mkF a h
addDefaultHelp dval (RawAbsPathOrStdArg s l mkF _ mkV unV a h)
| [dval] == map mkV (unV dval) = absPathOrStdArg s l mkF a (h++" [DEFAULT]")
| otherwise = absPathOrStdArg s l mkF a h
addDefaultHelp dval (RawOptAbsPathArg s l mkF _ mkV unV d a h)
| [dval] == map mkV (unV dval) = optAbsPathArg s l d mkF a (h++" [DEFAULT]")
| otherwise = optAbsPathArg s l d mkF a h
withDefault :: Eq v => v -> [RawOptSpec Flag v] -> PrimDarcsOption v
withDefault dval ropts = OptSpec {..} where
ounparse k = k . rawUnparse ropts
oparse k = k . defHead dval . map fst . rawParse ropts
ocheck fs = case map snd (rawParse ropts fs) of
[] -> []
[_] -> []
ropts' -> ["conflicting options: " ++ intercalate ", " (map (intercalate "/" . switchNames) ropts')]
odesc = map (addDefaultHelp dval) ropts
singleNoArg :: [Char] -> [String] -> Flag -> String -> PrimDarcsOption Bool
singleNoArg s l f h = withDefault False [RawNoArg s l f True h]
singleStrArg :: [Char] -> [String] -> (String -> Flag) -> (Flag -> Maybe String)
-> String -> String -> PrimDarcsOption (Maybe String)
singleStrArg s l mkf isf n h =
withDefault Nothing [ RawStrArg s l mkf (maybeToList . isf) Just maybeToList n h ]
singleAbsPathArg :: [Char] -> [String]
-> (AbsolutePath -> Flag) -> (Flag -> Maybe AbsolutePath)
-> String -> String -> PrimDarcsOption (Maybe AbsolutePath)
singleAbsPathArg s l mkf isf n h =
withDefault Nothing [ RawAbsPathArg s l mkf (maybeToList . isf) Just maybeToList n h ]
multiStrArg :: [Char] -> [String] -> (String -> Flag) -> ([Flag] -> [String])
-> String -> String -> PrimDarcsOption [String]
multiStrArg = multiArg strArg
multiOptStrArg :: [Char] -> [String] -> (Maybe String -> Flag)
-> ([Flag] -> [Maybe String]) -> String -> String
-> PrimDarcsOption [Maybe String]
multiOptStrArg = multiArg optStrArg
multiAbsPathArg :: [Char] -> [String] -> (AbsolutePath -> Flag) -> ([Flag] -> [AbsolutePath])
-> String -> String -> PrimDarcsOption [AbsolutePath]
multiAbsPathArg = multiArg absPathArg
multiArg :: SingleArgOptDescr a Flag
-> [Char] -> [String] -> (a -> Flag) -> ([Flag] -> [a])
-> String -> String -> PrimDarcsOption [a]
multiArg singleArg s l mkf isf n h = OptSpec {..} where
ounparse k xs = k [ mkf x | x <- xs ]
oparse k = k . isf
ocheck _ = []
odesc = [singleArg s l mkf n h]
deprecated :: [String] -> [RawOptSpec Flag v] -> PrimDarcsOption ()
deprecated comments ropts = OptSpec {..} where
ounparse k _ = k []
oparse k _ = k ()
ocheck fs = case map snd (rawParse ropts fs) of
[] -> []
ropts' -> ("deprecated option(s): " ++ intercalate ", " (concatMap switchNames ropts')) : comments
odesc = map noDefaultHelp ropts
noDefaultHelp (RawNoArg s l f _ h) = noArg s l f h
noDefaultHelp (RawStrArg s l mkF _ _ _ a h) = strArg s l mkF a h
noDefaultHelp (RawAbsPathArg s l mkF _ _ _ a h) = absPathArg s l mkF a h
noDefaultHelp (RawAbsPathOrStdArg s l mkF _ _ _ a h) = absPathOrStdArg s l mkF a h
noDefaultHelp (RawOptAbsPathArg s l mkF _ _ _ d a h) = optAbsPathArg s l d mkF a h
data ArgumentParseError = ArgumentParseError String String
deriving (Eq, Typeable)
instance Exception ArgumentParseError
instance Show ArgumentParseError where
show (ArgumentParseError arg expected) =
unwords ["cannot parse flag argument",show arg,"as",expected]
parseIntArg :: String -> (Int -> Bool) -> String -> Int
parseIntArg expected cond s =
case reads s of
(n,""):_ | cond n -> n
_ -> throw (ArgumentParseError s expected)
parseIndexRangeArg :: String -> (Int,Int)
parseIndexRangeArg s =
case reads s of
(n,""):_ | n > 0 -> (n,n)
(n,'-':s'):_ | n > 0, (m,""):_ <- reads s', m > 0 -> (n,m)
_ -> throw (ArgumentParseError s "index range")
showIntArg :: Int -> String
showIntArg = show
showIndexRangeArg :: (Int,Int) -> String
showIndexRangeArg (n,m) = show n ++ "-" ++ show m