{-# LANGUAGE OverloadedStrings #-}

module Poseidon.Chronicle where

import           Poseidon.EntityTypes   (HasNameAndVersion (..))
import           Poseidon.Package       (PoseidonPackage (..))
import           Poseidon.Utils         (Env (..), PoseidonException (..),
                                         PoseidonIO, TestMode (..))
import           Poseidon.Version       (VersionComponent (..),
                                         updateThreeComponentVersion)

import           Control.Monad.Catch    (throwM)
import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.Reader   (asks)
import           Data.Aeson             (FromJSON, ToJSON, object, parseJSON,
                                         toJSON, withObject, (.!=), (.:), (.:?),
                                         (.=))
import qualified Data.ByteString        as B
import           Data.Function          (on)
import           Data.List              (elemIndex)
import           Data.Maybe             (fromMaybe)
import qualified Data.Set               as S
import           Data.Time              (Day, UTCTime (..), getCurrentTime)
import           Data.Version           (Version, makeVersion)
import           Data.Yaml              (decodeEither')
import           Data.Yaml.Pretty       (defConfig, encodePretty,
                                         setConfCompare, setConfDropNull)
import           GitHash                (getGitInfo, giHash)
import           System.Directory       (createDirectoryIfMissing, makeAbsolute)
import           System.FilePath        (makeRelative, takeDirectory)

data PoseidonPackageChronicle = PoseidonPackageChronicle
    { PoseidonPackageChronicle -> String
snapYamlTitle            :: String
    , PoseidonPackageChronicle -> Maybe String
snapYamlDescription      :: Maybe String
    , PoseidonPackageChronicle -> Version
snapYamlChronicleVersion :: Version
    , PoseidonPackageChronicle -> Day
snapYamlLastModified     :: Day
    , PoseidonPackageChronicle -> Set PackageIteration
snapYamlPackages         :: S.Set PackageIteration
    }
    deriving (Int -> PoseidonPackageChronicle -> ShowS
[PoseidonPackageChronicle] -> ShowS
PoseidonPackageChronicle -> String
(Int -> PoseidonPackageChronicle -> ShowS)
-> (PoseidonPackageChronicle -> String)
-> ([PoseidonPackageChronicle] -> ShowS)
-> Show PoseidonPackageChronicle
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PoseidonPackageChronicle -> ShowS
showsPrec :: Int -> PoseidonPackageChronicle -> ShowS
$cshow :: PoseidonPackageChronicle -> String
show :: PoseidonPackageChronicle -> String
$cshowList :: [PoseidonPackageChronicle] -> ShowS
showList :: [PoseidonPackageChronicle] -> ShowS
Show, PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool
(PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool)
-> (PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool)
-> Eq PoseidonPackageChronicle
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool
== :: PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool
$c/= :: PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool
/= :: PoseidonPackageChronicle -> PoseidonPackageChronicle -> Bool
Eq)

instance FromJSON PoseidonPackageChronicle where
    parseJSON :: Value -> Parser PoseidonPackageChronicle
parseJSON = String
-> (Object -> Parser PoseidonPackageChronicle)
-> Value
-> Parser PoseidonPackageChronicle
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"PoseidonYamlStruct" ((Object -> Parser PoseidonPackageChronicle)
 -> Value -> Parser PoseidonPackageChronicle)
-> (Object -> Parser PoseidonPackageChronicle)
-> Value
-> Parser PoseidonPackageChronicle
forall a b. (a -> b) -> a -> b
$ \Object
v -> String
-> Maybe String
-> Version
-> Day
-> Set PackageIteration
-> PoseidonPackageChronicle
PoseidonPackageChronicle
        (String
 -> Maybe String
 -> Version
 -> Day
 -> Set PackageIteration
 -> PoseidonPackageChronicle)
-> Parser String
-> Parser
     (Maybe String
      -> Version
      -> Day
      -> Set PackageIteration
      -> PoseidonPackageChronicle)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"title"
        Parser
  (Maybe String
   -> Version
   -> Day
   -> Set PackageIteration
   -> PoseidonPackageChronicle)
-> Parser (Maybe String)
-> Parser
     (Version
      -> Day -> Set PackageIteration -> PoseidonPackageChronicle)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser (Maybe String)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"description"
        Parser
  (Version
   -> Day -> Set PackageIteration -> PoseidonPackageChronicle)
-> Parser Version
-> Parser (Day -> Set PackageIteration -> PoseidonPackageChronicle)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Version
forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"chronicleVersion"
        Parser (Day -> Set PackageIteration -> PoseidonPackageChronicle)
-> Parser Day
-> Parser (Set PackageIteration -> PoseidonPackageChronicle)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Day
forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"lastModified"
        Parser (Set PackageIteration -> PoseidonPackageChronicle)
-> Parser (Set PackageIteration) -> Parser PoseidonPackageChronicle
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser (Maybe (Set PackageIteration))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"packages" Parser (Maybe (Set PackageIteration))
-> Set PackageIteration -> Parser (Set PackageIteration)
forall a. Parser (Maybe a) -> a -> Parser a
.!= Set PackageIteration
forall a. Set a
S.empty

instance ToJSON PoseidonPackageChronicle where
    toJSON :: PoseidonPackageChronicle -> Value
toJSON PoseidonPackageChronicle
x = [Pair] -> Value
object ([Pair] -> Value) -> [Pair] -> Value
forall a b. (a -> b) -> a -> b
$ [
        Key
"title"            Key -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PoseidonPackageChronicle -> String
snapYamlTitle PoseidonPackageChronicle
x,
        Key
"description"      Key -> Maybe String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PoseidonPackageChronicle -> Maybe String
snapYamlDescription PoseidonPackageChronicle
x,
        Key
"chronicleVersion" Key -> Version -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PoseidonPackageChronicle -> Version
snapYamlChronicleVersion PoseidonPackageChronicle
x,
        Key
"lastModified"     Key -> Day -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PoseidonPackageChronicle -> Day
snapYamlLastModified PoseidonPackageChronicle
x] [Pair] -> [Pair] -> [Pair]
forall a. [a] -> [a] -> [a]
++
        if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Set PackageIteration -> Bool
forall a. Set a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (PoseidonPackageChronicle -> Set PackageIteration
snapYamlPackages PoseidonPackageChronicle
x) then [Key
"packages" Key -> Set PackageIteration -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PoseidonPackageChronicle -> Set PackageIteration
snapYamlPackages PoseidonPackageChronicle
x] else []

-- | A data type to represent a package state
data PackageIteration = PackageIteration
    { PackageIteration -> String
pacStateTitle   :: String   -- ^ the title of the package
    , PackageIteration -> Version
pacStateVersion :: Version  -- ^ the version of the package
    , PackageIteration -> String
pacStateCommit  :: String   -- ^ the hash of a relevant commit where a package can be accessed in this version
    , PackageIteration -> String
pacStatePath    :: FilePath -- ^ the file path where the POSEIDON.yml file is stored
    }
    deriving (Int -> PackageIteration -> ShowS
[PackageIteration] -> ShowS
PackageIteration -> String
(Int -> PackageIteration -> ShowS)
-> (PackageIteration -> String)
-> ([PackageIteration] -> ShowS)
-> Show PackageIteration
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PackageIteration -> ShowS
showsPrec :: Int -> PackageIteration -> ShowS
$cshow :: PackageIteration -> String
show :: PackageIteration -> String
$cshowList :: [PackageIteration] -> ShowS
showList :: [PackageIteration] -> ShowS
Show)

instance Eq PackageIteration where
    (PackageIteration String
t1 Version
v1 String
_ String
_) == :: PackageIteration -> PackageIteration -> Bool
== (PackageIteration String
t2 Version
v2 String
_ String
_) = (String
t1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
t2) Bool -> Bool -> Bool
&& (Version
v1 Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v2)

instance Ord PackageIteration where
    (PackageIteration String
t1 Version
v1 String
_ String
_) compare :: PackageIteration -> PackageIteration -> Ordering
`compare` (PackageIteration String
t2 Version
v2 String
_ String
_) = (String
t1,Version
v1) (String, Version) -> (String, Version) -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` (String
t2,Version
v2)

instance FromJSON PackageIteration where
    parseJSON :: Value -> Parser PackageIteration
parseJSON = String
-> (Object -> Parser PackageIteration)
-> Value
-> Parser PackageIteration
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"packages" ((Object -> Parser PackageIteration)
 -> Value -> Parser PackageIteration)
-> (Object -> Parser PackageIteration)
-> Value
-> Parser PackageIteration
forall a b. (a -> b) -> a -> b
$ \Object
v -> String -> Version -> String -> String -> PackageIteration
PackageIteration
        (String -> Version -> String -> String -> PackageIteration)
-> Parser String
-> Parser (Version -> String -> String -> PackageIteration)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"title"
        Parser (Version -> String -> String -> PackageIteration)
-> Parser Version -> Parser (String -> String -> PackageIteration)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser Version
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"version"
        Parser (String -> String -> PackageIteration)
-> Parser String -> Parser (String -> PackageIteration)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"commit"
        Parser (String -> PackageIteration)
-> Parser String -> Parser PackageIteration
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"path"

instance ToJSON PackageIteration where
    toJSON :: PackageIteration -> Value
toJSON PackageIteration
x = [Pair] -> Value
object [
          Key
"title"   Key -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PackageIteration -> String
pacStateTitle PackageIteration
x
        , Key
"version" Key -> Version -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PackageIteration -> Version
pacStateVersion PackageIteration
x
        , Key
"commit"  Key -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PackageIteration -> String
pacStateCommit PackageIteration
x
        , Key
"path"    Key -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
forall v. ToJSON v => Key -> v -> Pair
.= PackageIteration -> String
pacStatePath PackageIteration
x
        ]

instance HasNameAndVersion PackageIteration where
    getPacName :: PackageIteration -> String
getPacName (PackageIteration String
t Version
_ String
_ String
_) = String
t
    getPacVersion :: PackageIteration -> Maybe Version
getPacVersion (PackageIteration String
_ Version
v String
_ String
_) = Version -> Maybe Version
forall a. a -> Maybe a
Just Version
v

updateChronicle :: PoseidonPackageChronicle -> PoseidonPackageChronicle -> PoseidonPackageChronicle
updateChronicle :: PoseidonPackageChronicle
-> PoseidonPackageChronicle -> PoseidonPackageChronicle
updateChronicle PoseidonPackageChronicle
oldChronicle PoseidonPackageChronicle
newChronicle =
    let oldPackageSet :: Set PackageIteration
oldPackageSet = PoseidonPackageChronicle -> Set PackageIteration
snapYamlPackages PoseidonPackageChronicle
oldChronicle
        newPackageSet :: Set PackageIteration
newPackageSet = PoseidonPackageChronicle -> Set PackageIteration
snapYamlPackages PoseidonPackageChronicle
newChronicle
        mergedPacSet :: Set PackageIteration
mergedPacSet  = Set PackageIteration
-> Set PackageIteration -> Set PackageIteration
forall a. Ord a => Set a -> Set a -> Set a
S.union Set PackageIteration
oldPackageSet Set PackageIteration
newPackageSet
        oldChronicleVersion :: Version
oldChronicleVersion = PoseidonPackageChronicle -> Version
snapYamlChronicleVersion PoseidonPackageChronicle
oldChronicle
    in
        if Set PackageIteration
mergedPacSet Set PackageIteration -> Set PackageIteration -> Bool
forall a. Eq a => a -> a -> Bool
== Set PackageIteration
oldPackageSet
        then PoseidonPackageChronicle
oldChronicle
        else PoseidonPackageChronicle {
              snapYamlTitle :: String
snapYamlTitle            = PoseidonPackageChronicle -> String
snapYamlTitle PoseidonPackageChronicle
oldChronicle
            , snapYamlDescription :: Maybe String
snapYamlDescription      = PoseidonPackageChronicle -> Maybe String
snapYamlDescription PoseidonPackageChronicle
oldChronicle
            , snapYamlChronicleVersion :: Version
snapYamlChronicleVersion = VersionComponent -> Version -> Version
updateThreeComponentVersion VersionComponent
Minor Version
oldChronicleVersion
            , snapYamlLastModified :: Day
snapYamlLastModified     = PoseidonPackageChronicle -> Day
snapYamlLastModified PoseidonPackageChronicle
newChronicle
            , snapYamlPackages :: Set PackageIteration
snapYamlPackages         = Set PackageIteration
mergedPacSet
            }

readChronicle :: FilePath -> PoseidonIO PoseidonPackageChronicle
readChronicle :: String -> PoseidonIO PoseidonPackageChronicle
readChronicle String
p = do
    ByteString
bs <- IO ByteString -> ReaderT Env IO ByteString
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ByteString -> ReaderT Env IO ByteString)
-> IO ByteString -> ReaderT Env IO ByteString
forall a b. (a -> b) -> a -> b
$ String -> IO ByteString
B.readFile String
p
    case ByteString -> Either ParseException PoseidonPackageChronicle
forall a. FromJSON a => ByteString -> Either ParseException a
decodeEither' ByteString
bs of
        Left ParseException
err   -> PoseidonException -> PoseidonIO PoseidonPackageChronicle
forall e a. Exception e => e -> ReaderT Env IO a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (PoseidonException -> PoseidonIO PoseidonPackageChronicle)
-> PoseidonException -> PoseidonIO PoseidonPackageChronicle
forall a b. (a -> b) -> a -> b
$ String -> ParseException -> PoseidonException
PoseidonYamlParseException String
p ParseException
err
        Right PoseidonPackageChronicle
snap -> PoseidonPackageChronicle -> PoseidonIO PoseidonPackageChronicle
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return PoseidonPackageChronicle
snap

writeChronicle :: FilePath -> PoseidonPackageChronicle -> PoseidonIO ()
writeChronicle :: String -> PoseidonPackageChronicle -> PoseidonIO ()
writeChronicle String
p PoseidonPackageChronicle
snapShot = do
    IO () -> PoseidonIO ()
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PoseidonIO ()) -> IO () -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$ Bool -> String -> IO ()
createDirectoryIfMissing Bool
True (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ ShowS
takeDirectory String
p
    IO () -> PoseidonIO ()
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PoseidonIO ()) -> IO () -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$ String -> ByteString -> IO ()
B.writeFile String
p (Config -> PoseidonPackageChronicle -> ByteString
forall a. ToJSON a => Config -> a -> ByteString
encodePretty Config
opts PoseidonPackageChronicle
snapShot)
    where
        opts :: Config
opts = Bool -> Config -> Config
setConfDropNull Bool
True (Config -> Config) -> Config -> Config
forall a b. (a -> b) -> a -> b
$ (Text -> Text -> Ordering) -> Config -> Config
setConfCompare (Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Int -> Int -> Ordering)
-> (Text -> Int) -> Text -> Text -> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` Text -> Int
fieldIndex) Config
defConfig
        fieldIndex :: Text -> Int
fieldIndex Text
s = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe ([Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
fields) (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$ Text
s Text -> [Text] -> Maybe Int
forall a. Eq a => a -> [a] -> Maybe Int
`elemIndex` [Text]
fields
        fields :: [Text]
fields = [
            Text
"title",
            Text
"description",
            Text
"chronicleVersion",
            Text
"lastModified",
            Text
"packages",
            Text
"title",
            Text
"version",
            Text
"commit",
            Text
"path"
         ]

makeChronicle :: FilePath -> [PoseidonPackage] -> PoseidonIO PoseidonPackageChronicle
makeChronicle :: String -> [PoseidonPackage] -> PoseidonIO PoseidonPackageChronicle
makeChronicle String
pathToChronicleFile [PoseidonPackage]
pacs = do
    Set PackageIteration
pacChronicles <- String -> [PoseidonPackage] -> PoseidonIO (Set PackageIteration)
chroniclePackages String
pathToChronicleFile [PoseidonPackage]
pacs
    (UTCTime Day
today DiffTime
_) <- IO UTCTime -> ReaderT Env IO UTCTime
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UTCTime
getCurrentTime
    PoseidonPackageChronicle -> PoseidonIO PoseidonPackageChronicle
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (PoseidonPackageChronicle -> PoseidonIO PoseidonPackageChronicle)
-> PoseidonPackageChronicle -> PoseidonIO PoseidonPackageChronicle
forall a b. (a -> b) -> a -> b
$ PoseidonPackageChronicle {
      snapYamlTitle :: String
snapYamlTitle            = String
"Chronicle title"
    , snapYamlDescription :: Maybe String
snapYamlDescription      = String -> Maybe String
forall a. a -> Maybe a
Just String
"Chronicle description"
    , snapYamlChronicleVersion :: Version
snapYamlChronicleVersion = [Int] -> Version
makeVersion [Int
0, Int
1, Int
0]
    , snapYamlLastModified :: Day
snapYamlLastModified     = Day
today
    , snapYamlPackages :: Set PackageIteration
snapYamlPackages         = Set PackageIteration
pacChronicles
    }

chroniclePackages :: FilePath -> [PoseidonPackage] -> PoseidonIO (S.Set PackageIteration)
chroniclePackages :: String -> [PoseidonPackage] -> PoseidonIO (Set PackageIteration)
chroniclePackages String
pathToChronicleFile [PoseidonPackage]
pacs = do
    [PackageIteration]
pacStateList <- (PoseidonPackage -> ReaderT Env IO PackageIteration)
-> [PoseidonPackage] -> ReaderT Env IO [PackageIteration]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM PoseidonPackage -> ReaderT Env IO PackageIteration
snapOne [PoseidonPackage]
pacs
    Set PackageIteration -> PoseidonIO (Set PackageIteration)
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Set PackageIteration -> PoseidonIO (Set PackageIteration))
-> Set PackageIteration -> PoseidonIO (Set PackageIteration)
forall a b. (a -> b) -> a -> b
$ [PackageIteration] -> Set PackageIteration
forall a. Ord a => [a] -> Set a
S.fromList [PackageIteration]
pacStateList
    where
        snapOne :: PoseidonPackage -> PoseidonIO PackageIteration
        snapOne :: PoseidonPackage -> ReaderT Env IO PackageIteration
snapOne PoseidonPackage
pac = do
            Version
version <- PoseidonPackage -> PoseidonIO Version
getPackageVersion PoseidonPackage
pac
            String
commit <- String -> PoseidonIO String
getGitCommitHash (String -> PoseidonIO String) -> String -> PoseidonIO String
forall a b. (a -> b) -> a -> b
$ PoseidonPackage -> String
posPacBaseDir PoseidonPackage
pac
            PackageIteration -> ReaderT Env IO PackageIteration
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (PackageIteration -> ReaderT Env IO PackageIteration)
-> PackageIteration -> ReaderT Env IO PackageIteration
forall a b. (a -> b) -> a -> b
$ PackageIteration {
                pacStateTitle :: String
pacStateTitle   = PoseidonPackage -> String
forall a. HasNameAndVersion a => a -> String
getPacName PoseidonPackage
pac,
                pacStateVersion :: Version
pacStateVersion = Version
version,
                pacStateCommit :: String
pacStateCommit  = String
commit,
                pacStatePath :: String
pacStatePath    = String -> ShowS
makeRelative (ShowS
takeDirectory String
pathToChronicleFile) ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ PoseidonPackage -> String
posPacBaseDir PoseidonPackage
pac
            }

getPackageVersion :: PoseidonPackage -> PoseidonIO Version
getPackageVersion :: PoseidonPackage -> PoseidonIO Version
getPackageVersion PoseidonPackage
pac =
    case PoseidonPackage -> Maybe Version
forall a. HasNameAndVersion a => a -> Maybe Version
getPacVersion PoseidonPackage
pac of
        Just Version
v -> Version -> PoseidonIO Version
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Version
v
        Maybe Version
Nothing -> do
            TestMode
testMode <- (Env -> TestMode) -> ReaderT Env IO TestMode
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Env -> TestMode
_envTestMode
            case TestMode
testMode of
                TestMode
Testing -> Version -> PoseidonIO Version
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Version -> PoseidonIO Version) -> Version -> PoseidonIO Version
forall a b. (a -> b) -> a -> b
$ [Int] -> Version
makeVersion [Int
0, Int
0, Int
0]
                TestMode
Production -> do
                    PoseidonException -> PoseidonIO Version
forall e a. Exception e => e -> ReaderT Env IO a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (PoseidonException -> PoseidonIO Version)
-> PoseidonException -> PoseidonIO Version
forall a b. (a -> b) -> a -> b
$ String -> PoseidonException
PoseidonChronicleException (String -> PoseidonException) -> String -> PoseidonException
forall a b. (a -> b) -> a -> b
$
                        String
"Package " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show (PoseidonPackage -> String
forall a. HasNameAndVersion a => a -> String
getPacName PoseidonPackage
pac) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" has no version."

getGitCommitHash :: FilePath -> PoseidonIO String
getGitCommitHash :: String -> PoseidonIO String
getGitCommitHash String
p = do
    TestMode
testMode <- (Env -> TestMode) -> ReaderT Env IO TestMode
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks Env -> TestMode
_envTestMode
    case TestMode
testMode of
        TestMode
Testing -> String -> PoseidonIO String
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
"TestMode Git hash"
        TestMode
Production -> do
            Either GitHashException GitInfo
eitherGit <- IO (Either GitHashException GitInfo)
-> ReaderT Env IO (Either GitHashException GitInfo)
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either GitHashException GitInfo)
 -> ReaderT Env IO (Either GitHashException GitInfo))
-> IO (Either GitHashException GitInfo)
-> ReaderT Env IO (Either GitHashException GitInfo)
forall a b. (a -> b) -> a -> b
$ String -> IO (Either GitHashException GitInfo)
getGitInfo String
p
            case Either GitHashException GitInfo
eitherGit of
                Left GitHashException
_ -> do
                    String
pAbsolute <- IO String -> PoseidonIO String
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO String -> PoseidonIO String) -> IO String -> PoseidonIO String
forall a b. (a -> b) -> a -> b
$ String -> IO String
makeAbsolute String
p
                    let oneLevelUp :: String
oneLevelUp = ShowS
takeDirectory String
pAbsolute
                    if String
oneLevelUp String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== ShowS
takeDirectory String
oneLevelUp
                    then do String -> PoseidonIO String
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
"No Git repository found"
                    else String -> PoseidonIO String
getGitCommitHash String
oneLevelUp
                Right GitInfo
info -> do
                    String -> PoseidonIO String
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> PoseidonIO String) -> String -> PoseidonIO String
forall a b. (a -> b) -> a -> b
$ GitInfo -> String
giHash GitInfo
info