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.Maybe as Maybe
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 -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
Maybe.fromMaybe Bool
False (Maybe Bool -> Bool)
-> (Optional Bool -> Maybe Bool) -> Optional Bool -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional Bool -> Maybe Bool
forall a. Optional a -> Maybe a
Optional.toMaybe (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 -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
Maybe.fromMaybe Bool
False (Maybe Bool -> Bool)
-> (Optional Bool -> Maybe Bool) -> Optional Bool -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional Bool -> Maybe Bool
forall a. Optional a -> Maybe a
Optional.toMaybe (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 -> Maybe Input -> Input
forall a. a -> Maybe a -> a
Maybe.fromMaybe Input
Input.Stdin (Maybe Input -> Input)
-> (Optional Input -> Maybe Input) -> Optional Input -> Input
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional Input -> Maybe Input
forall a. Optional a -> Maybe a
Optional.toMaybe (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 -> Maybe Leniency -> Leniency
forall a. a -> Maybe a -> a
Maybe.fromMaybe Leniency
Leniency.Lenient (Maybe Leniency -> Leniency)
-> (Optional Leniency -> Maybe Leniency)
-> Optional Leniency
-> Leniency
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional Leniency -> Maybe Leniency
forall a. Optional a -> Maybe a
Optional.toMaybe (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 -> Maybe Mode -> Mode
forall a. a -> Maybe a -> a
Maybe.fromMaybe Mode
Mode.Format (Maybe Mode -> Mode)
-> (Optional Mode -> Maybe Mode) -> Optional Mode -> Mode
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional Mode -> Maybe Mode
forall a. Optional a -> Maybe a
Optional.toMaybe (Optional Mode -> Mode) -> Optional Mode -> Mode
forall a b. (a -> b) -> a -> b
$ Config -> Optional Mode
Config.mode Config
config,
        output :: Output
output = Output -> Maybe Output -> Output
forall a. a -> Maybe a -> a
Maybe.fromMaybe Output
Output.Stdout (Maybe Output -> Output)
-> (Optional Output -> Maybe Output) -> Optional Output -> Output
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional Output -> Maybe Output
forall a. Optional a -> Maybe a
Optional.toMaybe (Optional Output -> Output) -> Optional Output -> Output
forall a b. (a -> b) -> a -> b
$ Config -> Optional Output
Config.output Config
config,
        stdin :: FilePath
stdin = FilePath -> Maybe FilePath -> FilePath
forall a. a -> Maybe a -> a
Maybe.fromMaybe FilePath
filePath (Maybe FilePath -> FilePath)
-> (Optional FilePath -> Maybe FilePath)
-> Optional FilePath
-> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional FilePath -> Maybe FilePath
forall a. Optional a -> Maybe a
Optional.toMaybe (Optional FilePath -> FilePath) -> Optional FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ Config -> Optional FilePath
Config.stdin Config
config
      }