@@ -1,846 +0,0 @@
1−{-# OPTIONS_GHC -Wall #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−module CommandLine.Args.Internal
6− , Flags, noFlags, flags, (|--)
8− , Parser, int, file, anything, custom
9− , Args, noArgs, required, optional, zeroOrMore, oneOrMore, oneOf
10− , require0, require1, require2, require3, require4, require5
11− , RequiredArgs, args, exactly, (!), (?), (...)
17−import Data.Monoid ((<>))
18−import qualified Data.Char as Char
19−import qualified Data.List as List
20−import GHC.IO.Handle (hIsTerminalDevice)
21−import System.Environment (getArgs)
22−import System.Exit (exitFailure)
23−import System.IO (hPutStrLn, stderr)
24−import qualified Text.PrettyPrint.ANSI.Leijen as P
26−import Elm.Utils (nearbyNames)
34− Route :: (args -> flags -> a) -> Args args -> Flags flags -> Route a
42− FDone :: a -> Flags a
43− FMore :: Flags (a -> b) -> Flag a -> Flags b
56−(|--) :: Flags (a -> b) -> Flag a -> Flags b
66− Flag :: String -> Parser a -> String -> Flag (Maybe a)
67− OnOff :: String -> String -> Flag Bool
70−flag :: String -> Parser a -> String -> Flag (Maybe a)
75−onOff :: String -> String -> Flag Bool
85− Anything :: String -> Parser String
86− File :: Parser FilePath
87− Int :: String -> Parser Int
88− Custom :: String -> (String -> Maybe a) -> Parser a
91−file :: Parser FilePath
96−int :: String -> Parser Int
101−anything :: String -> Parser String
106−custom :: String -> (String -> Maybe a) -> Parser a
117−getParserInfo :: Parser a -> ParserInfo
118−getParserInfo parser =
133−runParser :: Parser a -> String -> Either ParserError a
134−runParser parser str =
143− if all Char.isDigit str then
144− Right (read str :: Int)
146− Left (Expecting tipe)
148− Custom tipe toMaybe ->
151− Left (Expecting tipe)
157−data ParserError = Expecting String
169−required :: Parser a -> Args a
174−optional :: Parser a -> Args (Maybe a)
180−zeroOrMore :: Parser a -> Args [a]
186−oneOrMore :: Parser a -> Args (a, [a])
193−require0 :: args -> Args args
195− exactly (args value)
198−require1 :: (a -> args) -> Parser a -> Args args
206−require2 :: (a -> b -> args) -> Parser a -> Parser b -> Args args
215−require3 :: (a -> b -> c -> args) -> Parser a -> Parser b -> Parser c -> Args args
216−require3 func a b c =
225−require4 :: (a -> b -> c -> d -> args) -> Parser a -> Parser b -> Parser c -> Parser d -> Args args
226−require4 func a b c d =
236−require5 :: (a -> b -> c -> d -> e -> args) -> Parser a -> Parser b -> Parser c -> Parser d -> Parser e -> Args args
237−require5 func a b c d e =
248−oneOf :: [Args a] -> Args a
257−data RequiredArgs a where
258− Done :: a -> RequiredArgs a
259− Required :: RequiredArgs (a -> b) -> Parser a -> RequiredArgs b
263− Exactly :: RequiredArgs a -> Args a
264− Optional :: RequiredArgs (Maybe a -> b) -> Parser a -> Args b
265− Multiple :: RequiredArgs ([a] -> b) -> Parser a -> Args b
266− OneOf :: [Args a] -> Args a
269−args :: a -> RequiredArgs a
274−exactly :: RequiredArgs a -> Args a
279−(!) :: RequiredArgs (a -> b) -> Parser a -> RequiredArgs b
284−(?) :: RequiredArgs (Maybe a -> b) -> Parser a -> Args b
289−(...) :: RequiredArgs ([a] -> b) -> Parser a -> Args b
298−get :: Route a -> [(String, Route a)] -> IO a
299−get route namedRoutes =
300− do chunks <- getArgs
301− case parse route namedRoutes chunks of
302− Right flagsAndArgs ->
314− = AfterCommand String RouteError
315− | BadCommand String [String] String
316− | NoCommand RouteError
319−parse :: Route a -> [(String, Route a)] -> [String] -> Either Error a
320−parse route namedRoutes chunks =
321− case parseHelp namedRoutes chunks [] of
326− case parseRoute route chunks of
332− (name, specificRouteError) : _ ->
333− Left (AfterCommand name specificRouteError)
337− first : _ | not (List.isPrefixOf "-" first) ->
338− case nearbyNames id first (map fst namedRoutes) of
340− Left (NoCommand routeError)
343− Left (BadCommand first (init suggestions) (last suggestions))
346− Left (NoCommand routeError)
349−parseHelp :: [(String, Route a)] -> [String] -> RouteErrors -> Either RouteErrors a
350−parseHelp namedRoutes chunks errors =
355− (name, route) : rest ->
356− case parseNamedRoute name route chunks of
358− parseHelp rest chunks (errs ++ errors)
368−type RouteErrors = [(String, RouteError)]
371−parseNamedRoute :: String -> Route a -> [String] -> Either RouteErrors a
372−parseNamedRoute name route rawChunks =
374− first : chunks | name == first ->
375− case parseRoute route chunks of
390−data RouteError = BadFlags FlagError | BadArgs ArgError
393−parseRoute :: Route a -> [String] -> Either RouteError a
394−parseRoute (Route func args_ flags_) chunks =
395− case parseFlags flags_ chunks of
397− Left (BadFlags flagError)
399− Right (flagData, remainingChunks) ->
400− case parseArgs args_ remainingChunks of
402− Left (BadArgs argError)
405− Right (func argData flagData)
414− | MissingArg ParserInfo
415− | BadArg String ParserError
416− | ExtraArgs [String]
419−parseArgs :: Args a -> [String] -> Either ArgError a
420−parseArgs args_ chunks =
423− parseOneOf argsList chunks []
425− Exactly requiredArgs ->
426− do (value, extras) <- parseRequiredArgs requiredArgs chunks
432− Left (ExtraArgs extras)
434− Optional requiredArgs optionalParser ->
435− do (func, subChunks) <- parseRequiredArgs requiredArgs chunks
438− Right (func Nothing)
441− do value <- runArgParser optionalParser chunk
442− Right (func (Just value))
445− Left (ExtraArgs extras)
447− Multiple requiredArgs multiParser ->
448− do (func, extras) <- parseRequiredArgs requiredArgs chunks
449− values <- traverse (runArgParser multiParser) extras
453−parseOneOf :: [Args a] -> [String] -> [ArgError] -> Either ArgError a
454−parseOneOf argsList chunks errors =
457− Left (NoneOf (reverse errors))
460− case parseArgs args_ chunks of
462− parseOneOf rest chunks (err:errors)
468−parseRequiredArgs :: RequiredArgs a -> [String] -> Either ArgError (a, [String])
469−parseRequiredArgs args_ chunks =
472− Right (value, chunks)
474− Required funcArgs parser ->
475− do (func, subChunks) <- parseRequiredArgs funcArgs chunks
478− Left (MissingArg (getParserInfo parser))
481− do value <- runArgParser parser chunk
482− Right ( func value, rest )
485−runArgParser :: Parser a -> String -> Either ArgError a
486−runArgParser parser chunk =
487− case runParser parser chunk of
489− Left (BadArg chunk parserError)
500− Either FlagError (a, [String])
504− = BadOnOff String String
505− | BadFlag String ParserInfo (Maybe (String, ParserError))
506− | Unrecognized String [(String, String)]
509−parseFlags :: Flags a -> [String] -> Result a
510−parseFlags flags_ chunks =
511− do (a, leftoverChunks) <- parseFlagsHelp flags_ chunks
512− case List.find (List.isPrefixOf "-") leftoverChunks of
514− Right (a, leftoverChunks)
517− Left (Unrecognized badArg (collectFlagInfo flags_))
520−parseFlagsHelp :: Flags a -> [String] -> Result a
521−parseFlagsHelp flags_ chunks =
524− Right (value, chunks)
526− FMore makeFunc flag_ ->
527− do (func, remainingChunks) <- parseFlagsHelp makeFunc chunks
528− (arg, leftoverChunks) <- parseFlag flag_ remainingChunks
529− Right (func arg, leftoverChunks)
532−parseFlag :: Flag a -> [String] -> Result a
533−parseFlag flag_ chunks =
535− Flag name parser _summary ->
536− parseFlagFlag name parser chunks
538− OnOff name _summary ->
539− parseFlagOnOff name chunks
542−parseFlagFlag :: String -> Parser a -> [String] -> Result (Maybe a)
543−parseFlagFlag name parser chunks =
544− case findDoubleDash [] ("--" ++ name) chunks of
546− Right (Nothing, chunks)
548− Just (ChunkZipper before value after) ->
551− parseFlagFlagHelp name parser before str after
554− parseFlagFlagHelp name parser before str after
557− Left (BadFlag name (getParserInfo parser) Nothing)
560−parseFlagFlagHelp :: String -> Parser a -> [String] -> String -> [String] -> Result (Maybe a)
561−parseFlagFlagHelp name parser before str after =
562− case runParser parser str of
564− Right (Just value, before ++ after)
567− Left $ BadFlag name (getParserInfo parser) $
568− Just (str, parserError)
571−parseFlagOnOff :: String -> [String] -> Result Bool
572−parseFlagOnOff name chunks =
573− case findDoubleDash [] ("--" ++ name) chunks of
575− Right (False, chunks)
577− Just (ChunkZipper before value after) ->
580− Right (True, before ++ after)
583− Right (True, before ++ str : after)
586− Left (BadOnOff name str)
595− { _before :: [String]
597− , _after :: [String]
607−findDoubleDash :: [String] -> String -> [String] -> Maybe ChunkZipper
608−findDoubleDash revPrev prefix chunks =
610− flag_ : val : rest | flag_ == prefix ->
611− if List.isPrefixOf "-" val then
612− Just $ ChunkZipper (reverse revPrev) DefNope (val : rest)
614− Just $ ChunkZipper (reverse revPrev) (Possibly val) rest
616− [flag_] | flag_ == prefix ->
617− Just $ ChunkZipper (reverse revPrev) DefNope []
619− flag_ : rest | List.isPrefixOf (prefix ++ "=") flag_ ->
622− drop (length prefix + 1) flag_
624− Just $ ChunkZipper (reverse revPrev) (Definitely value) rest
627− findDoubleDash (chunk : revPrev) prefix rest
634−-- COLLECT FLAG NAMES
637−collectFlagInfo :: Flags a -> [(String, String)]
638−collectFlagInfo flags_ =
639− collectFlagInfoHelp flags_ []
642−collectFlagInfoHelp :: Flags a -> [(String, String)] -> [(String, String)]
643−collectFlagInfoHelp flags_ info =
648− FMore otherFlags flag_ ->
649− collectFlagInfoHelp otherFlags (getFlagInfo flag_ : info)
652−getFlagInfo :: Flag a -> (String, String)
655− OnOff name summary ->
658− Flag name _ summary ->
666−failure :: Error -> IO a
668− do let doc = toDoc err
669− isTerminal <- hIsTerminalDevice stderr
670− P.displayIO stderr $ P.renderPretty 1 80 $
671− if isTerminal then doc else P.plain doc
676−toDoc :: Error -> P.Doc
679− BadCommand badName suggestions lastSuggestion ->
681− ["There","is","no",toRed badName,"command.","Try"
683− ++ oxfordify (map toGreen suggestions) "or" (toGreen lastSuggestion)
686− NoCommand (BadArgs argError) ->
687− argErrorToDoc Nothing argError
689− NoCommand (BadFlags flagError) ->
690− flagErrorToDoc Nothing flagError
692− AfterCommand name (BadArgs argError) ->
693− argErrorToDoc (Just name) argError
695− AfterCommand name (BadFlags flagError) ->
696− flagErrorToDoc (Just name) flagError
699−toRed :: String -> P.Doc
704−toGreen :: String -> P.Doc
706− P.green (P.text str)
713−argErrorToDoc :: Maybe String -> ArgError -> P.Doc
714−argErrorToDoc maybeCommand argError =
717− P.text $ "TODO - " ++ show (map (argErrorToDoc maybeCommand) argErrors)
719− MissingArg (ParserInfo tipe) ->
720− P.text $ "TODO - " ++ tipe
722− BadArg chunk _parserError ->
723− P.text $ "TODO - " ++ chunk
725− ExtraArgs extraChunks ->
726− P.text $ "TODO - " ++ show extraChunks
733−flagErrorToDoc :: Maybe String -> FlagError -> P.Doc
734−flagErrorToDoc maybeCommand flagError =
736− BadOnOff name value ->
739− "--" ++ name ++ "=" ++ value
743− "This on/off flag has a trailing equals sign:"
746− ["Just","get","rid","of","the","equals","sign,","like","this:"
747− , toGreen ("--" ++ name)
753− "This on/off flag was given a value:"
755− [ "An on/off flag either exists or not, no need for an equals sign and value.\n\
756− \Maybe you want this instead?"
757− , P.indent 4 (toGreen ("--" ++ name))
760− BadFlag name (ParserInfo tipe) Nothing ->
762− "This flag cannot appear without a value:"
764− [ P.fillSep ["It","needs","a",toGreen tipe,"value."]
767− BadFlag name (ParserInfo tipe) (Just (value, parserError)) ->
769− "This flag was given a bad value:"
770− ("--" ++ name ++ "=" ++ value)
772− ["I","need","a",toGreen tipe,"value,","but"
776− Expecting expectedType ->
777− [P.text value,"is","not","a","valid",P.text expectedType <> "."]
780− Unrecognized chunk [] ->
782− "Get rid of this flag:"
784− [ P.fillSep $ map P.text $ words $
785− case maybeCommand of
787− "I am not expecting any flags."
790− "The `" ++ command ++ "` command does not expect any flags."
793− Unrecognized chunk info ->
794− case takeWhile ('=' /=) (dropWhile ('-' ==) chunk) of
797− "This is not a valid flag:"
799− [ P.fillSep ["Valid","flags","look","like",P.green "--output=FILE"]
804− closeNames = nearbyNames id name (map fst info)
805− suggestions = if null closeNames then map fst info else closeNames
806− toFlag str = toGreen ("--" ++ str)
809− "I do not recognize this flag:"
817− [ P.fillSep ["Looks","like","a","typo.","Maybe","you","want",toFlag suggestion,"instead?"]
821− [ P.fillSep ["Maybe","you","want","one","of","these","instead?"]
822− , P.indent 4 (P.vcat (map toFlag suggestions))
827−flagErrorToDocHelp :: String -> String -> [P.Doc] -> P.Doc
828−flagErrorToDocHelp summary original explanation =
829− P.vcat $ concatMap (\d -> [d,""]) $
830− [ P.fillSep (map P.text (words summary))
831− , P.indent 4 (toRed original)
836−oxfordify :: [P.Doc] -> String -> P.Doc -> [P.Doc]
837−oxfordify chunks conjuction lastChunk =
843− [ chunk, P.text conjuction, lastChunk ]
846− map (<> P.comma) chunks ++ [ P.text conjuction, lastChunk ]