module CabalGild.Unstable.Type.Context where

import qualified CabalGild.Unstable.Class.MonadLog as MonadLog
import qualified CabalGild.Unstable.Exception.SpecifiedOutputWithCheckMode as SpecifiedOutputWithCheckMode
import qualified CabalGild.Unstable.Exception.SpecifiedStdinWithFileInput as SpecifiedStdinWithFileInput
import qualified CabalGild.Unstable.Type.Config as Config
import qualified CabalGild.Unstable.Type.Flag as Flag
import qualified CabalGild.Unstable.Type.Input as Input
import qualified CabalGild.Unstable.Type.Leniency as Leniency
import qualified CabalGild.Unstable.Type.Mode as Mode
import qualified CabalGild.Unstable.Type.Optional as Optional
import qualified CabalGild.Unstable.Type.Output as Output
import qualified Control.Monad as Monad
import qualified Control.Monad.Catch as Exception
import qualified Data.Char as Char
import qualified Data.List as List
import qualified Data.Version as Version
import qualified Paths_cabal_gild as This
import qualified System.Console.GetOpt as GetOpt
import qualified System.Exit as Exit

-- | Represents the context necessary to run the program. This is essentially a
-- simplified 'Config.Config'.
data Context = Context
  { Context -> Leniency
crlf :: Leniency.Leniency,
    Context -> Input
input :: Input.Input,
    Context -> Mode
mode :: Mode.Mode,
    Context -> Output
output :: Output.Output,
    Context -> FilePath
stdin :: FilePath
  }
  deriving (Context -> Context -> Bool
(Context -> Context -> Bool)
-> (Context -> Context -> Bool) -> Eq Context
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Context -> Context -> Bool
== :: Context -> Context -> Bool
$c/= :: Context -> Context -> Bool
/= :: Context -> Context -> Bool
Eq, Int -> Context -> ShowS
[Context] -> ShowS
Context -> FilePath
(Int -> Context -> ShowS)
-> (Context -> FilePath) -> ([Context] -> ShowS) -> Show Context
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Context -> ShowS
showsPrec :: Int -> Context -> ShowS
$cshow :: Context -> FilePath
show :: Context -> FilePath
$cshowList :: [Context] -> ShowS
showList :: [Context] -> ShowS
Show)

-- | Creates a 'Context' from a 'Config.Config'. If the help or version was
-- requested, then this will throw an 'Exit.ExitSuccess'. Otherwise this makes
-- sure the config is valid before returning the context.
fromConfig ::
  (MonadLog.MonadLog m, Exception.MonadThrow m) =>
  Config.Config ->
  m Context
fromConfig :: forall (m :: * -> *).
(MonadLog m, MonadThrow m) =>
Config -> m Context
fromConfig Config
config = do
  let version :: FilePath
version = Version -> FilePath
Version.showVersion Version
This.version

  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
Monad.when (Bool -> Optional Bool -> Bool
forall a. a -> Optional a -> a
Optional.withDefault Bool
False (Optional Bool -> Bool) -> Optional Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Config -> Optional Bool
Config.help Config
config) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
    let header :: FilePath
header =
          [FilePath] -> FilePath
unlines
            [ FilePath
"cabal-gild version " FilePath -> ShowS
forall a. Semigroup a => a -> a -> a
<> FilePath
version,
              FilePath
"",
              FilePath
"<https://github.com/tfausak/cabal-gild>"
            ]
    FilePath -> m ()
forall (m :: * -> *). MonadLog m => FilePath -> m ()
MonadLog.logLn
      (FilePath -> m ()) -> ShowS -> FilePath -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
List.dropWhileEnd Char -> Bool
Char.isSpace
      (FilePath -> m ()) -> FilePath -> m ()
forall a b. (a -> b) -> a -> b
$ FilePath -> [OptDescr Flag] -> FilePath
forall a. FilePath -> [OptDescr a] -> FilePath
GetOpt.usageInfo FilePath
header [OptDescr Flag]
Flag.options
    ExitCode -> m ()
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
Exception.throwM ExitCode
Exit.ExitSuccess

  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
Monad.when (Bool -> Optional Bool -> Bool
forall a. a -> Optional a -> a
Optional.withDefault Bool
False (Optional Bool -> Bool) -> Optional Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Config -> Optional Bool
Config.version Config
config) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
    FilePath -> m ()
forall (m :: * -> *). MonadLog m => FilePath -> m ()
MonadLog.logLn FilePath
version
    ExitCode -> m ()
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
Exception.throwM ExitCode
Exit.ExitSuccess

  case (Config -> Optional Input
Config.input Config
config, Config -> Optional FilePath
Config.stdin Config
config) of
    (Optional.Specific (Input.File FilePath
_), Optional.Specific FilePath
_) ->
      SpecifiedStdinWithFileInput -> m ()
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
Exception.throwM SpecifiedStdinWithFileInput
SpecifiedStdinWithFileInput.SpecifiedStdinWithFileInput
    (Optional Input, Optional FilePath)
_ -> () -> m ()
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

  case (Config -> Optional Mode
Config.mode Config
config, Config -> Optional Output
Config.output Config
config) of
    (Optional.Specific Mode
Mode.Check, Optional.Specific Output
_) ->
      SpecifiedOutputWithCheckMode -> m ()
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
Exception.throwM SpecifiedOutputWithCheckMode
SpecifiedOutputWithCheckMode.SpecifiedOutputWithCheckMode
    (Optional Mode, Optional Output)
_ -> () -> m ()
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

  let theInput :: Input
theInput = Input -> Optional Input -> Input
forall a. a -> Optional a -> a
Optional.withDefault Input
Input.Stdin (Optional Input -> Input) -> Optional Input -> Input
forall a b. (a -> b) -> a -> b
$ Config -> Optional Input
Config.input Config
config
      filePath :: FilePath
filePath = case Input
theInput of
        Input
Input.Stdin -> FilePath
"."
        Input.File FilePath
f -> FilePath
f
  Context -> m Context
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
    Context
      { crlf :: Leniency
crlf = Leniency -> Optional Leniency -> Leniency
forall a. a -> Optional a -> a
Optional.withDefault Leniency
Leniency.Lenient (Optional Leniency -> Leniency) -> Optional Leniency -> Leniency
forall a b. (a -> b) -> a -> b
$ Config -> Optional Leniency
Config.crlf Config
config,
        input :: Input
input = Input
theInput,
        mode :: Mode
mode = Mode -> Optional Mode -> Mode
forall a. a -> Optional a -> a
Optional.withDefault Mode
Mode.Format (Optional Mode -> Mode) -> Optional Mode -> Mode
forall a b. (a -> b) -> a -> b
$ Config -> Optional Mode
Config.mode Config
config,
        output :: Output
output = Output -> Optional Output -> Output
forall a. a -> Optional a -> a
Optional.withDefault Output
Output.Stdout (Optional Output -> Output) -> Optional Output -> Output
forall a b. (a -> b) -> a -> b
$ Config -> Optional Output
Config.output Config
config,
        stdin :: FilePath
stdin = FilePath -> Optional FilePath -> FilePath
forall a. a -> Optional a -> a
Optional.withDefault FilePath
filePath (Optional FilePath -> FilePath) -> Optional FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ Config -> Optional FilePath
Config.stdin Config
config
      }