never executed always true always false
    1 {-# LANGUAGE DataKinds #-}
    2 module ElmFormat.Cli (main, main') where
    3 
    4 import Prelude ()
    5 import Relude hiding (exitFailure, exitSuccess, putStr, putStrLn)
    6 
    7 import AST.Module (Module)
    8 import AST.Structure
    9 import AST.V0_16
   10 import CommandLine.Program (ProgramIO)
   11 import CommandLine.TransformFiles (TransformMode(..), ValidateMode(..))
   12 import CommandLine.World
   13 import ElmFormat.Messages
   14 import ElmVersion
   15 import Reporting.Annotation (Located)
   16 
   17 import qualified CommandLine.Program as Program
   18 import qualified CommandLine.ResolveFiles as ResolveFiles
   19 import qualified CommandLine.TransformFiles as TransformFiles
   20 import qualified Data.Text as Text
   21 import qualified ElmFormat.CliFlags as Flags
   22 import qualified ElmFormat.Parse as Parse
   23 import qualified ElmFormat.Render.Text as Render
   24 import qualified ElmFormat.Version
   25 import qualified Reporting.Result as Result
   26 import qualified ElmFormat.AST.PublicAST as PublicAST
   27 import qualified Data.Aeson as Aeson
   28 import qualified Data.ByteString.Char8 as B
   29 import qualified Data.ByteString.Lazy.Char8 as LB
   30 
   31 
   32 data WhatToDo
   33     = Format TransformMode
   34     | ConvertToJson TransformMode
   35     | ConvertFromJson TransformMode
   36     | Validate ValidateMode
   37 
   38 
   39 data Source
   40     = Stdin
   41     | FromFiles FilePath [FilePath]
   42 
   43 
   44 data Destination
   45     = InPlace
   46     | ToFile FilePath
   47 
   48 
   49 data Mode
   50     = FormatMode
   51     | JsonMode Flags.JsonMode
   52     | ValidateMode
   53 
   54 
   55 determineSource :: Bool -> Either [ResolveFiles.Error] [FilePath] -> Either ErrorMessage Source
   56 determineSource stdin inputFiles =
   57     case ( stdin, inputFiles ) of
   58         ( _, Left fileErrors ) -> Left $ BadInputFiles fileErrors
   59         ( True, Right [] ) -> Right Stdin
   60         ( False, Right [] ) -> Left NoInputs
   61         ( False, Right (first:rest) ) -> Right $ FromFiles first rest
   62         ( True, Right (_:_) ) -> Left TooManyInputs
   63 
   64 
   65 determineDestination :: Maybe FilePath -> Either ErrorMessage Destination
   66 determineDestination output =
   67     case output of
   68         Nothing -> Right InPlace
   69         Just path -> Right $ ToFile path
   70 
   71 
   72 determineMode :: Bool -> Maybe Flags.JsonMode -> Either ErrorMessage Mode
   73 determineMode doValidate json =
   74     case ( doValidate, json ) of
   75         ( False, Nothing ) -> Right FormatMode
   76         ( True, Nothing ) -> Right ValidateMode
   77         ( False, Just jsonMode ) -> Right (JsonMode jsonMode)
   78         _ -> Left OutputAndValidate
   79 
   80 
   81 determineWhatToDo :: Source -> Destination -> Mode -> Either ErrorMessage WhatToDo
   82 determineWhatToDo source destination mode =
   83     case ( mode, source, destination ) of
   84         ( ValidateMode, _, ToFile _) -> Left OutputAndValidate
   85         ( ValidateMode, Stdin, _ ) -> Right $ Validate ValidateStdin
   86         ( ValidateMode, FromFiles first rest, _) -> Right $ Validate (ValidateFiles first rest)
   87         ( FormatMode, Stdin, InPlace ) -> Right $ Format StdinToStdout
   88         ( FormatMode, Stdin, ToFile output ) -> Right $ Format (StdinToFile output)
   89         ( FormatMode, FromFiles first [], ToFile output ) -> Right $ Format (FileToFile first output)
   90         ( FormatMode, FromFiles first rest, InPlace ) -> Right $ Format (FilesInPlace first rest)
   91         ( JsonMode Flags.ElmToJson, Stdin, InPlace ) -> Right $ ConvertToJson StdinToStdout
   92         ( JsonMode Flags.ElmToJson, Stdin, ToFile output ) -> Right $ ConvertToJson (StdinToFile output)
   93         ( JsonMode Flags.ElmToJson, FromFiles first [], InPlace ) -> Right $ ConvertToJson (FileToStdout first)
   94         ( JsonMode Flags.ElmToJson, FromFiles _ (_:_), _ ) -> error "TODO: --json with multiple files is not supported"
   95         ( JsonMode Flags.JsonToElm, Stdin, InPlace ) -> Right $ ConvertFromJson StdinToStdout
   96         ( JsonMode Flags.JsonToElm, Stdin, ToFile output ) -> Right $ ConvertFromJson (StdinToFile output)
   97         ( JsonMode Flags.JsonToElm, FromFiles first [], InPlace ) -> Right $ ConvertFromJson (FileToStdout first)
   98         ( JsonMode Flags.JsonToElm, FromFiles _ (_:_), _ ) -> error "TODO: --from-json with multiple files is not supported"
   99         ( _, FromFiles _ _, ToFile _ ) -> Left SingleOutputWithMultipleInputs
  100 
  101 
  102 determineWhatToDoFromConfig :: Flags.Config -> Either [ResolveFiles.Error] [FilePath] -> Either ErrorMessage WhatToDo
  103 determineWhatToDoFromConfig config resolvedInputFiles =
  104     do
  105         source <- determineSource (Flags._stdin config) resolvedInputFiles
  106         destination <- determineDestination (Flags._output config)
  107         mode <- determineMode (Flags._validate config) (Flags._json config)
  108         determineWhatToDo source destination mode
  109 
  110 
  111 main :: World m => [String] -> m ()
  112 main =
  113     main'
  114         ElmFormat.Version.asString
  115         ElmFormat.Version.experimental
  116 
  117 main' :: World m => String -> Maybe String -> [String] -> m ()
  118 main' elmFormatVersion experimental args =
  119     Program.run (Flags.parser elmFormatVersion experimental) run' args
  120     where
  121         run' :: World m => Flags.Config -> ProgramIO m ErrorMessage ()
  122         run' flags =
  123             do
  124                 resolvedInputFiles <- Program.liftM $ ResolveFiles.resolveElmFiles (Flags._input flags)
  125 
  126                 whatToDo <- case determineWhatToDoFromConfig flags resolvedInputFiles of
  127                     Left NoInputs -> Program.showUsage
  128                     Left err -> Program.error err
  129                     Right a -> return a
  130 
  131                 elmVersion <- case Flags._elmVersion flags of
  132                     Just v -> return v
  133                     Nothing -> Program.liftME autoDetectElmVersion
  134 
  135                 let autoYes = Flags._yes flags
  136                 result <- Program.liftM $ doIt elmVersion autoYes whatToDo
  137                 if result
  138                     then return ()
  139                     else Program.failed
  140 
  141 
  142 autoDetectElmVersion :: World m => m (Either x ElmVersion)
  143 autoDetectElmVersion =
  144     do
  145         hasElmPackageJson <- doesFileExist "elm-package.json"
  146         if hasElmPackageJson
  147             then
  148                 do
  149                     hasElmJson <- doesFileExist "elm.json"
  150                     if hasElmJson
  151                         then return $ Right Elm_0_19
  152                         else return $ Right Elm_0_18
  153             else return $ Right Elm_0_19
  154 
  155 
  156 validate :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage ()
  157 validate elmVersion input@(inputFile, inputText) =
  158     case parseModule elmVersion input of
  159         Right modu ->
  160             if inputText /= Render.render elmVersion modu then
  161                 Left $ FileWouldChange elmVersion inputFile
  162             else
  163                 Right ()
  164 
  165         Left err ->
  166             Left err
  167 
  168 
  169 parseModule ::
  170     ElmVersion
  171     -> (FilePath, Text.Text)
  172     -> Either InfoMessage (Module [UppercaseIdentifier] (ASTNS Located [UppercaseIdentifier] 'TopLevelNK))
  173 parseModule elmVersion (inputFile, inputText) =
  174     case Parse.parse elmVersion inputText of
  175         Result.Result _ (Result.Ok modu) ->
  176             Right modu
  177 
  178         Result.Result _ (Result.Err errs) ->
  179             Left $ ParseError inputFile errs
  180 
  181 
  182 parseJson :: (FilePath, Text.Text)
  183     -> Either InfoMessage (Module [UppercaseIdentifier] (ASTNS Identity [UppercaseIdentifier] 'TopLevelNK))
  184 parseJson (inputFile, inputText) =
  185     case Aeson.eitherDecode (LB.fromChunks . return . encodeUtf8 $ inputText) of
  186         Right modu -> Right $ PublicAST.toModule modu
  187 
  188         Left message ->
  189             Left $ JsonParseError inputFile (Text.pack message)
  190 
  191 
  192 format :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage Text.Text
  193 format elmVersion input =
  194     Render.render elmVersion <$> parseModule elmVersion input
  195 
  196 
  197 toJson :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage Text.Text
  198 toJson elmVersion (inputFile, inputText) =
  199     let
  200         config =
  201             PublicAST.Config
  202                 { PublicAST.showSourceLocation = True
  203                 }
  204     in
  205     decodeUtf8 . B.concat . LB.toChunks . Aeson.encode . PublicAST.fromModule config
  206     <$> parseModule elmVersion (inputFile, inputText)
  207 
  208 
  209 fromJson :: ElmVersion -> (FilePath, Text.Text) -> Either InfoMessage Text.Text
  210 fromJson elmVersion input =
  211     Render.render elmVersion <$> parseJson input
  212 
  213 
  214 doIt :: World m => ElmVersion -> Bool -> WhatToDo -> m Bool
  215 doIt elmVersion autoYes whatToDo =
  216     case whatToDo of
  217         Validate validateMode ->
  218             TransformFiles.validateNoChanges
  219                 ProcessingFile
  220                 (validate elmVersion)
  221                 validateMode
  222 
  223         Format transformMode ->
  224             TransformFiles.applyTransformation
  225                 ProcessingFile autoYes FilesWillBeOverwritten
  226                 (format elmVersion)
  227                 transformMode
  228 
  229         ConvertToJson transformMode ->
  230             TransformFiles.applyTransformation
  231                 ProcessingFile autoYes FilesWillBeOverwritten
  232                 (toJson elmVersion)
  233                 transformMode
  234 
  235         ConvertFromJson transformMode ->
  236             TransformFiles.applyTransformation
  237                 ProcessingFile autoYes FilesWillBeOverwritten
  238                 (fromJson elmVersion)
  239                 transformMode