-- |
-- Module      : AOC.Run.Interactive
-- Copyright   : (c) Justin Le 2018
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- Versions of loaders and runners meant to be used in GHCI.
--

module AOC.Run.Interactive (
  -- * Fetch and Run
  -- ** Return Answers
    execSolution
  , execSolutionWith
  , testSolution
  , viewPrompt
  , waitForPrompt
  , submitSolution
  -- ** No Answers
  , execSolution_
  , execSolutionWith_
  , testSolution_
  , viewPrompt_
  , waitForPrompt_
  , submitSolution_
  -- * Load Inputs
  , loadInput
  , loadParseInput
  , loadTests
  , loadParseTests
  -- * Util
  , mkSpec
  ) where

import           AOC.Challenge
import           AOC.Run
import           AOC.Run.Config
import           AOC.Run.Load
import           AOC.Solver
import           AOC.Util
import           Advent
import           Control.Monad.Except
import           Data.Bifunctor
import           Data.Text            (Text)

-- | Run the solution indicated by the challenge spec on the official
-- puzzle input.  Get answer as result.
execSolution :: ChallengeSpec -> IO String
execSolution :: ChallengeSpec -> IO String
execSolution ChallengeSpec
cs = ExceptT [String] IO String -> IO String
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO String -> IO String)
-> ExceptT [String] IO String -> IO String
forall a b. (a -> b) -> a -> b
$ do
    Config
cfg <- IO Config -> ExceptT [String] IO Config
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Config -> ExceptT [String] IO Config)
-> IO Config -> ExceptT [String] IO Config
forall a b. (a -> b) -> a -> b
$ String -> IO Config
configFile String
defConfPath
    Map Day (Map Part (Maybe Bool, Either [String] String))
out <- Config
-> MainRunOpts
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall (m :: * -> *).
(MonadIO m, MonadError [String] m) =>
Config
-> MainRunOpts
-> m (Map Day (Map Part (Maybe Bool, Either [String] String)))
mainRun Config
cfg (MainRunOpts
 -> ExceptT
      [String]
      IO
      (Map Day (Map Part (Maybe Bool, Either [String] String))))
-> (TestSpec -> MainRunOpts)
-> TestSpec
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestSpec -> MainRunOpts
defaultMRO (TestSpec
 -> ExceptT
      [String]
      IO
      (Map Day (Map Part (Maybe Bool, Either [String] String))))
-> TestSpec
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall a b. (a -> b) -> a -> b
$ ChallengeSpec -> TestSpec
TSDayPart ChallengeSpec
cs
    (Maybe Bool, Either [String] String)
res <- [String]
-> Maybe (Maybe Bool, Either [String] String)
-> ExceptT [String] IO (Maybe Bool, Either [String] String)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither [String
"Result not found in result map (Internal Error)"] (Maybe (Maybe Bool, Either [String] String)
 -> ExceptT [String] IO (Maybe Bool, Either [String] String))
-> Maybe (Maybe Bool, Either [String] String)
-> ExceptT [String] IO (Maybe Bool, Either [String] String)
forall a b. (a -> b) -> a -> b
$
      ChallengeSpec
-> Map Day (Map Part (Maybe Bool, Either [String] String))
-> Maybe (Maybe Bool, Either [String] String)
forall a. ChallengeSpec -> Map Day (Map Part a) -> Maybe a
lookupSolution ChallengeSpec
cs Map Day (Map Part (Maybe Bool, Either [String] String))
out
    Either [String] String -> ExceptT [String] IO String
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither (Either [String] String -> ExceptT [String] IO String)
-> Either [String] String -> ExceptT [String] IO String
forall a b. (a -> b) -> a -> b
$ (Maybe Bool, Either [String] String) -> Either [String] String
forall a b. (a, b) -> b
snd (Maybe Bool, Either [String] String)
res

-- | Run the solution indicated by the challenge spec on a custom input.
-- Get answer as result.
execSolutionWith
    :: ChallengeSpec
    -> String               -- ^ custom puzzle input
    -> IO String
execSolutionWith :: ChallengeSpec -> String -> IO String
execSolutionWith ChallengeSpec
cs String
inp = ExceptT [String] IO String -> IO String
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO String -> IO String)
-> ExceptT [String] IO String -> IO String
forall a b. (a -> b) -> a -> b
$ do
    Config
cfg <- IO Config -> ExceptT [String] IO Config
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Config -> ExceptT [String] IO Config)
-> IO Config -> ExceptT [String] IO Config
forall a b. (a -> b) -> a -> b
$ String -> IO Config
configFile String
defConfPath
    Map Day (Map Part (Maybe Bool, Either [String] String))
out <- Config
-> MainRunOpts
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall (m :: * -> *).
(MonadIO m, MonadError [String] m) =>
Config
-> MainRunOpts
-> m (Map Day (Map Part (Maybe Bool, Either [String] String)))
mainRun Config
cfg (MainRunOpts
 -> ExceptT
      [String]
      IO
      (Map Day (Map Part (Maybe Bool, Either [String] String))))
-> MainRunOpts
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall a b. (a -> b) -> a -> b
$ (TestSpec -> MainRunOpts
defaultMRO (ChallengeSpec -> TestSpec
TSDayPart ChallengeSpec
cs))
      { _mroInput :: Day -> Part -> IO (Maybe String)
_mroInput = \Day
_ Part
_ -> Maybe String -> IO (Maybe String)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String -> Maybe String
forall a. a -> Maybe a
Just String
inp
      }
    (Maybe Bool, Either [String] String)
res <- [String]
-> Maybe (Maybe Bool, Either [String] String)
-> ExceptT [String] IO (Maybe Bool, Either [String] String)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither [String
"Result not found in result map (Internal Error)"] (Maybe (Maybe Bool, Either [String] String)
 -> ExceptT [String] IO (Maybe Bool, Either [String] String))
-> Maybe (Maybe Bool, Either [String] String)
-> ExceptT [String] IO (Maybe Bool, Either [String] String)
forall a b. (a -> b) -> a -> b
$
      ChallengeSpec
-> Map Day (Map Part (Maybe Bool, Either [String] String))
-> Maybe (Maybe Bool, Either [String] String)
forall a. ChallengeSpec -> Map Day (Map Part a) -> Maybe a
lookupSolution ChallengeSpec
cs Map Day (Map Part (Maybe Bool, Either [String] String))
out
    Either [String] String -> ExceptT [String] IO String
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither (Either [String] String -> ExceptT [String] IO String)
-> Either [String] String -> ExceptT [String] IO String
forall a b. (a -> b) -> a -> b
$ (Maybe Bool, Either [String] String) -> Either [String] String
forall a b. (a, b) -> b
snd (Maybe Bool, Either [String] String)
res

-- | Run test suite for a given challenge spec.
--
-- Returns 'Just' if any tests were run, with a 'Bool' specifying whether
-- or not all tests passed.
testSolution :: ChallengeSpec -> IO (Maybe Bool)
testSolution :: ChallengeSpec -> IO (Maybe Bool)
testSolution ChallengeSpec
cs = ExceptT [String] IO (Maybe Bool) -> IO (Maybe Bool)
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO (Maybe Bool) -> IO (Maybe Bool))
-> ExceptT [String] IO (Maybe Bool) -> IO (Maybe Bool)
forall a b. (a -> b) -> a -> b
$ do
    Config
cfg <- IO Config -> ExceptT [String] IO Config
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Config -> ExceptT [String] IO Config)
-> IO Config -> ExceptT [String] IO Config
forall a b. (a -> b) -> a -> b
$ String -> IO Config
configFile String
defConfPath
    Map Day (Map Part (Maybe Bool, Either [String] String))
out <- Config
-> MainRunOpts
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall (m :: * -> *).
(MonadIO m, MonadError [String] m) =>
Config
-> MainRunOpts
-> m (Map Day (Map Part (Maybe Bool, Either [String] String)))
mainRun Config
cfg (MainRunOpts
 -> ExceptT
      [String]
      IO
      (Map Day (Map Part (Maybe Bool, Either [String] String))))
-> MainRunOpts
-> ExceptT
     [String]
     IO
     (Map Day (Map Part (Maybe Bool, Either [String] String)))
forall a b. (a -> b) -> a -> b
$ (TestSpec -> MainRunOpts
defaultMRO (ChallengeSpec -> TestSpec
TSDayPart ChallengeSpec
cs))
      { _mroTest :: Bool
_mroTest  = Bool
True
      }
    (Maybe Bool, Either [String] String)
res <- [String]
-> Maybe (Maybe Bool, Either [String] String)
-> ExceptT [String] IO (Maybe Bool, Either [String] String)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither [String
"Result not found in result map (Internal Error)"] (Maybe (Maybe Bool, Either [String] String)
 -> ExceptT [String] IO (Maybe Bool, Either [String] String))
-> Maybe (Maybe Bool, Either [String] String)
-> ExceptT [String] IO (Maybe Bool, Either [String] String)
forall a b. (a -> b) -> a -> b
$
      ChallengeSpec
-> Map Day (Map Part (Maybe Bool, Either [String] String))
-> Maybe (Maybe Bool, Either [String] String)
forall a. ChallengeSpec -> Map Day (Map Part a) -> Maybe a
lookupSolution ChallengeSpec
cs Map Day (Map Part (Maybe Bool, Either [String] String))
out
    pure $ (Maybe Bool, Either [String] String) -> Maybe Bool
forall a b. (a, b) -> a
fst (Maybe Bool, Either [String] String)
res

-- | View the prompt for a given challenge spec.
viewPrompt :: ChallengeSpec -> IO Text
viewPrompt :: ChallengeSpec -> IO Text
viewPrompt ChallengeSpec
cs = ExceptT [String] IO Text -> IO Text
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO Text -> IO Text)
-> ExceptT [String] IO Text -> IO Text
forall a b. (a -> b) -> a -> b
$ do
    Config
cfg <- IO Config -> ExceptT [String] IO Config
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Config -> ExceptT [String] IO Config)
-> IO Config -> ExceptT [String] IO Config
forall a b. (a -> b) -> a -> b
$ String -> IO Config
configFile String
defConfPath
    Map Day (Map Part Text)
out <- Config
-> MainViewOpts -> ExceptT [String] IO (Map Day (Map Part Text))
forall (m :: * -> *).
(MonadIO m, MonadError [String] m) =>
Config -> MainViewOpts -> m (Map Day (Map Part Text))
mainView Config
cfg (MainViewOpts -> ExceptT [String] IO (Map Day (Map Part Text)))
-> (TestSpec -> MainViewOpts)
-> TestSpec
-> ExceptT [String] IO (Map Day (Map Part Text))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestSpec -> MainViewOpts
defaultMVO (TestSpec -> ExceptT [String] IO (Map Day (Map Part Text)))
-> TestSpec -> ExceptT [String] IO (Map Day (Map Part Text))
forall a b. (a -> b) -> a -> b
$ ChallengeSpec -> TestSpec
TSDayPart ChallengeSpec
cs
    [String] -> Maybe Text -> ExceptT [String] IO Text
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither [String
"Prompt not found in result map (Internal Error)"] (Maybe Text -> ExceptT [String] IO Text)
-> Maybe Text -> ExceptT [String] IO Text
forall a b. (a -> b) -> a -> b
$
      ChallengeSpec -> Map Day (Map Part Text) -> Maybe Text
forall a. ChallengeSpec -> Map Day (Map Part a) -> Maybe a
lookupSolution ChallengeSpec
cs Map Day (Map Part Text)
out

-- | Countdown to get the prompt for a given challenge spec, if not yet
-- available.
waitForPrompt :: ChallengeSpec -> IO Text
waitForPrompt :: ChallengeSpec -> IO Text
waitForPrompt ChallengeSpec
cs = ExceptT [String] IO Text -> IO Text
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO Text -> IO Text)
-> ExceptT [String] IO Text -> IO Text
forall a b. (a -> b) -> a -> b
$ do
    Config
cfg <- IO Config -> ExceptT [String] IO Config
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Config -> ExceptT [String] IO Config)
-> IO Config -> ExceptT [String] IO Config
forall a b. (a -> b) -> a -> b
$ String -> IO Config
configFile String
defConfPath
    Map Day (Map Part Text)
out <- Config
-> MainViewOpts -> ExceptT [String] IO (Map Day (Map Part Text))
forall (m :: * -> *).
(MonadIO m, MonadError [String] m) =>
Config -> MainViewOpts -> m (Map Day (Map Part Text))
mainView Config
cfg (MainViewOpts -> ExceptT [String] IO (Map Day (Map Part Text)))
-> MainViewOpts -> ExceptT [String] IO (Map Day (Map Part Text))
forall a b. (a -> b) -> a -> b
$ (TestSpec -> MainViewOpts
defaultMVO (ChallengeSpec -> TestSpec
TSDayPart ChallengeSpec
cs))
      { _mvoWait :: Bool
_mvoWait = Bool
True
      }
    [String] -> Maybe Text -> ExceptT [String] IO Text
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither [String
"Prompt not found in result map (Internal Error)"] (Maybe Text -> ExceptT [String] IO Text)
-> Maybe Text -> ExceptT [String] IO Text
forall a b. (a -> b) -> a -> b
$
      ChallengeSpec -> Map Day (Map Part Text) -> Maybe Text
forall a. ChallengeSpec -> Map Day (Map Part a) -> Maybe a
lookupSolution ChallengeSpec
cs Map Day (Map Part Text)
out

-- | Submit solution for a given challenge spec, lock if correct, and retry
-- if a 'wait' is received.
submitSolution :: ChallengeSpec -> IO (Text, SubmitRes)
submitSolution :: ChallengeSpec -> IO (Text, SubmitRes)
submitSolution ChallengeSpec
cs = ExceptT [String] IO (Text, SubmitRes) -> IO (Text, SubmitRes)
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO (Text, SubmitRes) -> IO (Text, SubmitRes))
-> ExceptT [String] IO (Text, SubmitRes) -> IO (Text, SubmitRes)
forall a b. (a -> b) -> a -> b
$ do
    Config
cfg <- IO Config -> ExceptT [String] IO Config
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Config -> ExceptT [String] IO Config)
-> IO Config -> ExceptT [String] IO Config
forall a b. (a -> b) -> a -> b
$ String -> IO Config
configFile String
defConfPath
    Config -> MainSubmitOpts -> ExceptT [String] IO (Text, SubmitRes)
forall (m :: * -> *).
(MonadIO m, MonadError [String] m) =>
Config -> MainSubmitOpts -> m (Text, SubmitRes)
mainSubmit Config
cfg (MainSubmitOpts -> ExceptT [String] IO (Text, SubmitRes))
-> MainSubmitOpts -> ExceptT [String] IO (Text, SubmitRes)
forall a b. (a -> b) -> a -> b
$ (ChallengeSpec -> MainSubmitOpts
defaultMSO ChallengeSpec
cs) { _msoRetry :: Bool
_msoRetry = Bool
True }

-- | Result-suppressing version of 'execSolution'.
execSolution_ :: ChallengeSpec -> IO ()
execSolution_ :: ChallengeSpec -> IO ()
execSolution_ = IO String -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO String -> IO ())
-> (ChallengeSpec -> IO String) -> ChallengeSpec -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChallengeSpec -> IO String
execSolution

-- | Result-suppressing version of 'execSolutionWith'.
execSolutionWith_
    :: ChallengeSpec
    -> String               -- ^ custom puzzle input
    -> IO ()
execSolutionWith_ :: ChallengeSpec -> String -> IO ()
execSolutionWith_ ChallengeSpec
cs = IO String -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO String -> IO ()) -> (String -> IO String) -> String -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChallengeSpec -> String -> IO String
execSolutionWith ChallengeSpec
cs

-- | Result-suppressing version of 'testSolution'.
testSolution_ :: ChallengeSpec -> IO ()
testSolution_ :: ChallengeSpec -> IO ()
testSolution_ = IO (Maybe Bool) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Maybe Bool) -> IO ())
-> (ChallengeSpec -> IO (Maybe Bool)) -> ChallengeSpec -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChallengeSpec -> IO (Maybe Bool)
testSolution

-- | Result-suppressing version of 'viewPrompt'.
viewPrompt_ :: ChallengeSpec -> IO ()
viewPrompt_ :: ChallengeSpec -> IO ()
viewPrompt_ = IO Text -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Text -> IO ())
-> (ChallengeSpec -> IO Text) -> ChallengeSpec -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChallengeSpec -> IO Text
viewPrompt

-- | Result-suppressing version of 'waitForPrompt'.
waitForPrompt_ :: ChallengeSpec -> IO ()
waitForPrompt_ :: ChallengeSpec -> IO ()
waitForPrompt_ = IO Text -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Text -> IO ())
-> (ChallengeSpec -> IO Text) -> ChallengeSpec -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChallengeSpec -> IO Text
waitForPrompt

-- | Result-suppressing version of 'submitSolution'.
submitSolution_ :: ChallengeSpec -> IO ()
submitSolution_ :: ChallengeSpec -> IO ()
submitSolution_ = IO (Text, SubmitRes) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Text, SubmitRes) -> IO ())
-> (ChallengeSpec -> IO (Text, SubmitRes))
-> ChallengeSpec
-> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ChallengeSpec -> IO (Text, SubmitRes)
submitSolution

-- | Run the parser of a solution, given its 'ChallengeSpec'.
--
-- @
-- 'loadParseInput' (solSpec 'day01a) day01a
-- @
loadParseInput :: ChallengeSpec -> a :~> b -> IO a
loadParseInput :: forall a b. ChallengeSpec -> (a :~> b) -> IO a
loadParseInput ChallengeSpec
cs a :~> b
s = ExceptT [String] IO a -> IO a
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO a -> IO a) -> ExceptT [String] IO a -> IO a
forall a b. (a -> b) -> a -> b
$ do
    String
i <- IO String -> ExceptT [String] IO String
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO String -> ExceptT [String] IO String)
-> IO String -> ExceptT [String] IO String
forall a b. (a -> b) -> a -> b
$ ChallengeSpec -> IO String
loadInput ChallengeSpec
cs
    [String] -> Maybe a -> ExceptT [String] IO a
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither [String
"No parse"] (Maybe a -> ExceptT [String] IO a)
-> Maybe a -> ExceptT [String] IO a
forall a b. (a -> b) -> a -> b
$ (a :~> b) -> String -> Maybe a
forall a b. (a :~> b) -> String -> Maybe a
sParse a :~> b
s String
i

-- | Run the parser of a solution on test data, given its 'ChallengeSpec'.
--
-- @
-- 'loadParseTests' (solSpec 'day01a) day01a
-- @
loadParseTests :: ChallengeSpec -> a :~> b -> IO [(Maybe a, TestMeta)]
loadParseTests :: forall a b. ChallengeSpec -> (a :~> b) -> IO [(Maybe a, TestMeta)]
loadParseTests ChallengeSpec
cs a :~> b
s = (((String, TestMeta) -> (Maybe a, TestMeta))
-> [(String, TestMeta)] -> [(Maybe a, TestMeta)]
forall a b. (a -> b) -> [a] -> [b]
map (((String, TestMeta) -> (Maybe a, TestMeta))
 -> [(String, TestMeta)] -> [(Maybe a, TestMeta)])
-> ((String -> Maybe a)
    -> (String, TestMeta) -> (Maybe a, TestMeta))
-> (String -> Maybe a)
-> [(String, TestMeta)]
-> [(Maybe a, TestMeta)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Maybe a) -> (String, TestMeta) -> (Maybe a, TestMeta)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first) ((a :~> b) -> String -> Maybe a
forall a b. (a :~> b) -> String -> Maybe a
sParse a :~> b
s) ([(String, TestMeta)] -> [(Maybe a, TestMeta)])
-> IO [(String, TestMeta)] -> IO [(Maybe a, TestMeta)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ChallengeSpec -> IO [(String, TestMeta)]
loadTests ChallengeSpec
cs

-- | Load input for a given challenge
loadInput :: ChallengeSpec -> IO String
loadInput :: ChallengeSpec -> IO String
loadInput ChallengeSpec
cs = ExceptT [String] IO String -> IO String
forall a. ExceptT [String] IO a -> IO a
eitherIO (ExceptT [String] IO String -> IO String)
-> ExceptT [String] IO String -> IO String
forall a b. (a -> b) -> a -> b
$ do
    CD{[(String, TestMeta)]
Maybe String
Either [String] String
Either [String] Text
_cdTests :: ChallengeData -> [(String, TestMeta)]
_cdAnswer :: ChallengeData -> Maybe String
_cdInput :: ChallengeData -> Either [String] String
_cdPrompt :: ChallengeData -> Either [String] Text
_cdTests :: [(String, TestMeta)]
_cdAnswer :: Maybe String
_cdInput :: Either [String] String
_cdPrompt :: Either [String] Text
..}  <- IO ChallengeData -> ExceptT [String] IO ChallengeData
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ChallengeData -> ExceptT [String] IO ChallengeData)
-> IO ChallengeData -> ExceptT [String] IO ChallengeData
forall a b. (a -> b) -> a -> b
$ do
      Cfg{Integer
Maybe String
_cfgYear :: Config -> Integer
_cfgSession :: Config -> Maybe String
_cfgYear :: Integer
_cfgSession :: Maybe String
..} <- String -> IO Config
configFile String
defConfPath
      Maybe String -> Integer -> ChallengeSpec -> IO ChallengeData
challengeData Maybe String
_cfgSession Integer
_cfgYear ChallengeSpec
cs
    Either [String] String -> ExceptT [String] IO String
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
liftEither Either [String] String
_cdInput

-- | Load test cases for a given challenge
loadTests :: ChallengeSpec -> IO [(String, TestMeta)]
loadTests :: ChallengeSpec -> IO [(String, TestMeta)]
loadTests ChallengeSpec
cs = do
    Cfg{Integer
Maybe String
_cfgYear :: Integer
_cfgSession :: Maybe String
_cfgYear :: Config -> Integer
_cfgSession :: Config -> Maybe String
..} <- String -> IO Config
configFile String
defConfPath
    ChallengeData -> [(String, TestMeta)]
_cdTests (ChallengeData -> [(String, TestMeta)])
-> IO ChallengeData -> IO [(String, TestMeta)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String -> Integer -> ChallengeSpec -> IO ChallengeData
challengeData Maybe String
_cfgSession Integer
_cfgYear ChallengeSpec
cs

-- | Unsafely create a 'ChallengeSpec' from a day number and part.
--
-- Is undefined if given a day number out of range (1-25).
mkSpec :: Integer -> Part -> ChallengeSpec
mkSpec :: Integer -> Part -> ChallengeSpec
mkSpec Integer
i = Day -> Part -> ChallengeSpec
CS (Integer -> Day
mkDay_ Integer
i)

eitherIO :: ExceptT [String] IO a -> IO a
eitherIO :: forall a. ExceptT [String] IO a -> IO a
eitherIO ExceptT [String] IO a
act = ExceptT [String] IO a -> IO (Either [String] a)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT ExceptT [String] IO a
act IO (Either [String] a) -> (Either [String] a -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Right a
x  -> a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
    Left  [String]
es -> String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> IO a) -> String -> IO a
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines [String]
es