module Hix.Managed.Handlers.Report.Prod where

import qualified Data.Text as Text
import Data.These (These (..))
import Data.These.Combinators (justHere, justThere)
import Exon (exon)

import qualified Hix.Console
import Hix.Console (color, colors)
import Hix.Data.Monad (M)
import Hix.Data.Version (Version)
import qualified Hix.Data.VersionBounds
import Hix.Data.VersionBounds (VersionBounds (VersionBounds))
import qualified Hix.Log as Log
import qualified Hix.Managed.Data.EnvResult
import Hix.Managed.Data.EnvResult (EnvResult (EnvResult))
import Hix.Managed.Data.Mutable (MutableDep)
import qualified Hix.Managed.Data.MutableId
import Hix.Managed.Data.MutableId (MutableId (MutableId))
import qualified Hix.Managed.Data.Mutation
import Hix.Managed.Data.Mutation (FailedMutation)
import qualified Hix.Managed.Data.ProjectResult as ProjectResult
import Hix.Managed.Data.ProjectResult (ProjectResult)
import Hix.Managed.Data.StageResult (StageFailure (FailedMutations, FailedPrecondition), StageSummary (..))
import qualified Hix.Managed.EnvResult
import qualified Hix.Managed.EnvResult as EnvResult
import Hix.Managed.EnvResult (
  BoundsModification (BoundsModification),
  DepModification (DepAdded, DepUpdated),
  DepResult (DepResult),
  DepResultDetail (..),
  DepResults (DepResults),
  )
import qualified Hix.Managed.Handlers.Report
import Hix.Managed.Handlers.Report (ReportHandlers (ReportHandlers))
import Hix.Pretty (showP)

blankLine :: M ()
blankLine :: M ()
blankLine = Text -> M ()
Log.infoPlain Text
""

listFailed ::
  FailedMutation ->
  Text
listFailed :: FailedMutation -> Text
listFailed FailedMutation
mutation = [exon|📦 #{showP mutation.package}|]

reportFailed ::
  NonEmpty FailedMutation ->
  M ()
reportFailed :: NonEmpty FailedMutation -> M ()
reportFailed =
  (FailedMutation -> M ()) -> NonEmpty FailedMutation -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ \ FailedMutation
mut -> Text -> M ()
Log.infoCont (FailedMutation -> Text
listFailed FailedMutation
mut)

reportNewVersions ::
  NonEmpty MutableId ->
  M ()
reportNewVersions :: NonEmpty MutableId -> M ()
reportNewVersions =
  (MutableId -> M ()) -> NonEmpty MutableId -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ \ MutableId {MutableDep
name :: MutableDep
name :: MutableId -> MutableDep
name, Version
version :: Version
version :: MutableId -> Version
version} ->
    Text -> M ()
Log.infoCont [exon|📦 '##{name}': #{showP version}|]

printSummary :: StageSummary -> M ()
printSummary :: StageSummary -> M ()
printSummary = \case
  StageSuccess Text
msg -> Text -> M ()
Log.info Text
msg
  StageFailure (FailedMutations Text
msg NonEmpty FailedMutation
failed) -> do
    Text -> M ()
Log.info Text
msg
    NonEmpty FailedMutation -> M ()
reportFailed NonEmpty FailedMutation
failed
  StageFailure (FailedPrecondition NonEmpty Text
msg) ->
    (Text -> M ()) -> NonEmpty Text -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ Text -> M ()
Log.info NonEmpty Text
msg
  StageNoAction Maybe Text
msg -> (Text -> M ()) -> Maybe Text -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ Text -> M ()
Log.info Maybe Text
msg
  StageReport Text
msg NonEmpty MutableId
versions -> do
    Text -> M ()
Log.info Text
msg
    NonEmpty MutableId -> M ()
reportNewVersions NonEmpty MutableId
versions

printBounds ::
  (Version -> Text) ->
  (Version -> Text) ->
  Maybe Version ->
  Maybe Version ->
  Text
printBounds :: (Version -> Text)
-> (Version -> Text) -> Maybe Version -> Maybe Version -> Text
printBounds Version -> Text
printL Version -> Text
printU Maybe Version
lower Maybe Version
upper =
  case (Maybe Version
lower, Maybe Version
upper) of
    (Just Version
l, Just Version
u) -> [exon|[#{printL l}, #{printU u}]|]
    (Just Version
l, Maybe Version
Nothing) -> [exon|>=#{printL l}|]
    (Maybe Version
Nothing, Just Version
u) -> [exon|<#{printU u}|]
    (Maybe Version
Nothing, Maybe Version
Nothing) -> Text
"[no bounds]"

formatDepLine :: MutableDep -> Text -> Text -> (Text, Text, Text)
formatDepLine :: MutableDep -> Text -> Text -> (Text, Text, Text)
formatDepLine MutableDep
package Text
version Text
bounds =
  ([exon|📦 #{color colors.blue (showP package)}|], Text
version, [exon|↕ #{bounds}|])

formatBoundsModification :: VersionBounds -> BoundsModification -> Text
formatBoundsModification :: VersionBounds -> BoundsModification -> Text
formatBoundsModification VersionBounds {lower :: VersionBounds -> Maybe Version
lower = Maybe Version
lowerNew, upper :: VersionBounds -> Maybe Version
upper = Maybe Version
upperNew} (BoundsModification These (Maybe Version) (Maybe Version)
bm) =
  [exon|#{original} -> #{new}|]
  where

    original :: Text
original = Maybe Version -> Maybe Version -> Int -> Bool -> Bool -> Text
interval Maybe Version
lowerOriginal Maybe Version
upperOriginal ColorOffsets
colors.red Bool
lowerChanged Bool
upperChanged
    new :: Text
new = Maybe Version -> Maybe Version -> Int -> Bool -> Bool -> Text
interval Maybe Version
lowerNew Maybe Version
upperNew ColorOffsets
colors.green Bool
lowerChanged Bool
upperChanged

    lowerOriginal :: Maybe Version
lowerOriginal = Maybe Version -> Maybe (Maybe Version) -> Maybe Version
forall a. a -> Maybe a -> a
fromMaybe Maybe Version
lowerNew Maybe (Maybe Version)
lowerDiff
    upperOriginal :: Maybe Version
upperOriginal = Maybe Version -> Maybe (Maybe Version) -> Maybe Version
forall a. a -> Maybe a -> a
fromMaybe Maybe Version
upperNew Maybe (Maybe Version)
upperDiff

    lowerChanged :: Bool
lowerChanged = Maybe (Maybe Version) -> Bool
forall a. Maybe a -> Bool
isJust Maybe (Maybe Version)
lowerDiff
    upperChanged :: Bool
upperChanged = Maybe (Maybe Version) -> Bool
forall a. Maybe a -> Bool
isJust Maybe (Maybe Version)
upperDiff

    lowerDiff :: Maybe (Maybe Version)
lowerDiff = These (Maybe Version) (Maybe Version) -> Maybe (Maybe Version)
forall a b. These a b -> Maybe a
justHere These (Maybe Version) (Maybe Version)
bm
    upperDiff :: Maybe (Maybe Version)
upperDiff = These (Maybe Version) (Maybe Version) -> Maybe (Maybe Version)
forall a b. These a b -> Maybe b
justThere These (Maybe Version) (Maybe Version)
bm

    interval :: Maybe Version -> Maybe Version -> Int -> Bool -> Bool -> Text
interval Maybe Version
l Maybe Version
u Int
col Bool
colL Bool
colU =
      (Version -> Text)
-> (Version -> Text) -> Maybe Version -> Maybe Version -> Text
printBounds (Int -> Bool -> Version -> Text
forall {a}. Pretty a => Int -> Bool -> a -> Text
bound Int
col Bool
colL) (Int -> Bool -> Version -> Text
forall {a}. Pretty a => Int -> Bool -> a -> Text
bound Int
col Bool
colU) Maybe Version
l Maybe Version
u

    bound :: Int -> Bool -> a -> Text
bound Int
col Bool
useCol a
v
      | Bool
useCol
      = Int -> Text -> Text
color Int
col (a -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP a
v)
      | Bool
otherwise
      = a -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP a
v

formatDep :: MutableDep -> Version -> VersionBounds -> Maybe BoundsModification -> (Text, Text, Text)
formatDep :: MutableDep
-> Version
-> VersionBounds
-> Maybe BoundsModification
-> (Text, Text, Text)
formatDep MutableDep
package Version
version VersionBounds
bounds Maybe BoundsModification
boundsMod =
  MutableDep -> Text -> Text -> (Text, Text, Text)
formatDepLine MutableDep
package (Version -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP Version
version) (Text
-> (BoundsModification -> Text) -> Maybe BoundsModification -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
unchangedBounds BoundsModification -> Text
changedBounds Maybe BoundsModification
boundsMod)
  where
    changedBounds :: BoundsModification -> Text
changedBounds = VersionBounds -> BoundsModification -> Text
formatBoundsModification VersionBounds
bounds
    unchangedBounds :: Text
unchangedBounds = (Version -> Text)
-> (Version -> Text) -> Maybe Version -> Maybe Version -> Text
printBounds Version -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP Version -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP VersionBounds
bounds.lower VersionBounds
bounds.upper

formatDepUpdate ::
  MutableDep ->
  Version ->
  VersionBounds ->
  These Version BoundsModification ->
  (Text, Text, Text)
formatDepUpdate :: MutableDep
-> Version
-> VersionBounds
-> These Version BoundsModification
-> (Text, Text, Text)
formatDepUpdate MutableDep
package Version
version VersionBounds
bounds These Version BoundsModification
update =
  MutableDep -> Text -> Text -> (Text, Text, Text)
formatDepLine MutableDep
package Text
versionDesc Text
boundsDesc
  where
    versionDesc :: Text
versionDesc = case These Version BoundsModification -> Maybe Version
forall a b. These a b -> Maybe a
justHere These Version BoundsModification
update of
      Just Version
original -> Version -> Version -> Text
forall {a} {a}. (Pretty a, Pretty a) => a -> a -> Text
versionUpdate Version
original Version
version
      Maybe Version
Nothing -> Version -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP Version
version

    versionUpdate :: a -> a -> Text
versionUpdate a
original a
new =
      [exon|#{color colors.red (showP original)} -> #{color colors.green (showP new)}|]

    boundsDesc :: Text
boundsDesc = case These Version BoundsModification -> Maybe BoundsModification
forall a b. These a b -> Maybe b
justThere These Version BoundsModification
update of
      Just BoundsModification
boundsMod -> VersionBounds -> BoundsModification -> Text
formatBoundsModification VersionBounds
bounds BoundsModification
boundsMod
      Maybe BoundsModification
Nothing -> VersionBounds -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP VersionBounds
bounds

printAligned :: [(Text, Text, Text)] -> M ()
printAligned :: [(Text, Text, Text)] -> M ()
printAligned [(Text, Text, Text)]
deps =
  ((Text, Text, Text) -> M ()) -> [(Text, Text, Text)] -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ (Text, Text, Text) -> M ()
printLine [(Text, Text, Text)]
deps
  where
    printLine :: (Text, Text, Text) -> M ()
printLine (Text
p, Text
v, Text
b) = Text -> M ()
Log.infoCont (Text -> Int -> Text
padded Text
p Int
maxP Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Int -> Text
padded Text
v Int
maxV Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
b)
    padded :: Text -> Int -> Text
padded Text
s Int
maxlen = Text
s Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text -> Text
Text.replicate (Int
maxlen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Text -> Int
Text.length Text
s Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
3) Text
" "
    maxP :: Int
maxP = [Text] -> Int
maxi [Text]
ps
    maxV :: Int
maxV = [Text] -> Int
maxi [Text]
vs
    maxi :: [Text] -> Int
maxi = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
0 (Maybe Int -> Int) -> ([Text] -> Maybe Int) -> [Text] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Int] -> Maybe Int
forall (t :: * -> *) a. (Ord a, Foldable t) => t a -> Maybe a
maximum ([Int] -> Maybe Int) -> ([Text] -> [Int]) -> [Text] -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Int) -> [Text] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Int
Text.length
    ([Text]
ps, [Text]
vs, [Text]
_) = [(Text, Text, Text)] -> ([Text], [Text], [Text])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 [(Text, Text, Text)]
deps

formatDepResult :: DepResult -> Maybe (Text, Text, Text)
formatDepResult :: DepResult -> Maybe (Text, Text, Text)
formatDepResult DepResult {Version
VersionBounds
MutableDep
DepResultDetail
package :: MutableDep
version :: Version
bounds :: VersionBounds
detail :: DepResultDetail
detail :: DepResult -> DepResultDetail
bounds :: DepResult -> VersionBounds
version :: DepResult -> Version
package :: DepResult -> MutableDep
..} =
  case DepResultDetail
detail of
    DepModified (DepAdded Maybe BoundsModification
boundsMod) -> (Text, Text, Text) -> Maybe (Text, Text, Text)
forall a. a -> Maybe a
Just (MutableDep
-> Version
-> VersionBounds
-> Maybe BoundsModification
-> (Text, Text, Text)
formatDep MutableDep
package Version
version VersionBounds
bounds Maybe BoundsModification
boundsMod)
    DepModified (DepUpdated These Version BoundsModification
update) -> (Text, Text, Text) -> Maybe (Text, Text, Text)
forall a. a -> Maybe a
Just (MutableDep
-> Version
-> VersionBounds
-> These Version BoundsModification
-> (Text, Text, Text)
formatDepUpdate MutableDep
package Version
version VersionBounds
bounds These Version BoundsModification
update)
    DepResultDetail
DepUnmodified -> Maybe (Text, Text, Text)
forall a. Maybe a
Nothing

-- TODO don't print the env name when using @sets = "all"@ (or even when only one env is processed?)
envResult ::
  EnvResult ->
  M ()
envResult :: EnvResult -> M ()
envResult result :: EnvResult
result@EnvResult {EnvName
env :: EnvName
env :: EnvResult -> EnvName
env, NonEmpty StageSummary
summaries :: NonEmpty StageSummary
summaries :: EnvResult -> NonEmpty StageSummary
summaries} = do
  Text -> M ()
Log.info Text
envLabel
  (StageSummary -> M ()) -> NonEmpty StageSummary -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
(a -> f b) -> t a -> f ()
traverse_ StageSummary -> M ()
printSummary NonEmpty StageSummary
summaries
  Maybe (NonEmpty DepResult) -> (NonEmpty DepResult -> M ()) -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ ([DepResult] -> Maybe (NonEmpty DepResult)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty [DepResult]
added) \ NonEmpty DepResult
deps -> do
    Text -> M ()
Log.info Text
"Added new versions:"
    [(Text, Text, Text)] -> M ()
printAligned ((DepResult -> Maybe (Text, Text, Text))
-> [DepResult] -> [(Text, Text, Text)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe DepResult -> Maybe (Text, Text, Text)
formatDepResult (NonEmpty DepResult -> [DepResult]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty DepResult
deps))
  Maybe (NonEmpty DepResult) -> (NonEmpty DepResult -> M ()) -> M ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ ([DepResult] -> Maybe (NonEmpty DepResult)
forall a. [a] -> Maybe (NonEmpty a)
nonEmpty [DepResult]
updated) \ NonEmpty DepResult
deps -> do
    Text -> M ()
Log.info Text
"Updated versions:"
    [(Text, Text, Text)] -> M ()
printAligned ((DepResult -> Maybe (Text, Text, Text))
-> [DepResult] -> [(Text, Text, Text)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe DepResult -> Maybe (Text, Text, Text)
formatDepResult (NonEmpty DepResult -> [DepResult]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty DepResult
deps))
  where
    DepResults {[DepResult]
added :: [DepResult]
updated :: [DepResult]
unmodified :: [DepResult]
unmodified :: DepResults -> [DepResult]
updated :: DepResults -> [DepResult]
added :: DepResults -> [DepResult]
..} = EnvResult -> DepResults
EnvResult.grouped EnvResult
result
    envLabel :: Text
envLabel = Int -> Text -> Text
color ColorOffsets
colors.yellow (EnvName -> Text
forall b a. (Pretty a, IsString b) => a -> b
showP EnvName
env)

mutations ::
  ProjectResult ->
  M ()
mutations :: ProjectResult -> M ()
mutations ProjectResult
results = do
  M ()
blankLine
  [M ()] -> M ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (M () -> [M ()] -> [M ()]
forall a. a -> [a] -> [a]
intersperse M ()
blankLine (EnvResult -> M ()
envResult (EnvResult -> M ()) -> [EnvResult] -> [M ()]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty EnvResult -> [EnvResult]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList ProjectResult
results.envs))

handlersProd :: ReportHandlers
handlersProd :: ReportHandlers
handlersProd =
  ReportHandlers {ProjectResult -> M ()
mutations :: ProjectResult -> M ()
mutations :: ProjectResult -> M ()
mutations}