{-# LANGUAGE Rank2Types #-}
module Distribution.Solver.Modular.RetryLog
    ( RetryLog
    , toProgress
    , fromProgress
    , mapFailure
    , retry
    , failWith
    , succeedWith
    , continueWith
    , tryWith
    ) where

import Distribution.Solver.Compat.Prelude
import Prelude ()

import Distribution.Solver.Modular.Message
import Distribution.Solver.Types.Progress

-- | 'Progress' as a difference list that allows efficient appends at failures.
newtype RetryLog step fail done = RetryLog {
    forall step fail done.
RetryLog step fail done
-> forall fail2.
   (fail -> Progress step fail2 done) -> Progress step fail2 done
unRetryLog :: forall fail2 . (fail -> Progress step fail2 done)
               -> Progress step fail2 done
  }

-- | /O(1)/. Convert a 'RetryLog' to a 'Progress'.
toProgress :: RetryLog step fail done -> Progress step fail done
toProgress :: forall step fail done.
RetryLog step fail done -> Progress step fail done
toProgress (RetryLog forall fail2.
(fail -> Progress step fail2 done) -> Progress step fail2 done
f) = (fail -> Progress step fail done) -> Progress step fail done
forall fail2.
(fail -> Progress step fail2 done) -> Progress step fail2 done
f fail -> Progress step fail done
forall step fail done. fail -> Progress step fail done
Fail

-- | /O(N)/. Convert a 'Progress' to a 'RetryLog'.
fromProgress :: Progress step fail done -> RetryLog step fail done
fromProgress :: forall step fail done.
Progress step fail done -> RetryLog step fail done
fromProgress Progress step fail done
l = (forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail -> Progress step fail2 done) -> Progress step fail2 done)
 -> RetryLog step fail done)
-> (forall fail2.
    (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall a b. (a -> b) -> a -> b
$ \fail -> Progress step fail2 done
f -> (fail -> Progress step fail2 done)
-> Progress step fail done -> Progress step fail2 done
forall fail1 step fail2 done.
(fail1 -> Progress step fail2 done)
-> Progress step fail1 done -> Progress step fail2 done
go fail -> Progress step fail2 done
f Progress step fail done
l
  where
    go :: (fail1 -> Progress step fail2 done)
       -> Progress step fail1 done
       -> Progress step fail2 done
    go :: forall fail1 step fail2 done.
(fail1 -> Progress step fail2 done)
-> Progress step fail1 done -> Progress step fail2 done
go fail1 -> Progress step fail2 done
_ (Done done
d) = done -> Progress step fail2 done
forall step fail done. done -> Progress step fail done
Done done
d
    go fail1 -> Progress step fail2 done
f (Fail fail1
failure) = fail1 -> Progress step fail2 done
f fail1
failure
    go fail1 -> Progress step fail2 done
f (Step step
m Progress step fail1 done
ms) = step -> Progress step fail2 done -> Progress step fail2 done
forall step fail done.
step -> Progress step fail done -> Progress step fail done
Step step
m ((fail1 -> Progress step fail2 done)
-> Progress step fail1 done -> Progress step fail2 done
forall fail1 step fail2 done.
(fail1 -> Progress step fail2 done)
-> Progress step fail1 done -> Progress step fail2 done
go fail1 -> Progress step fail2 done
f Progress step fail1 done
ms)

-- | /O(1)/. Apply a function to the failure value in a log.
mapFailure :: (fail1 -> fail2)
           -> RetryLog step fail1 done
           -> RetryLog step fail2 done
mapFailure :: forall fail1 fail2 step done.
(fail1 -> fail2)
-> RetryLog step fail1 done -> RetryLog step fail2 done
mapFailure fail1 -> fail2
f RetryLog step fail1 done
l = RetryLog step fail1 done
-> (fail1 -> RetryLog step fail2 done) -> RetryLog step fail2 done
forall step fail1 done fail2.
RetryLog step fail1 done
-> (fail1 -> RetryLog step fail2 done) -> RetryLog step fail2 done
retry RetryLog step fail1 done
l ((fail1 -> RetryLog step fail2 done) -> RetryLog step fail2 done)
-> (fail1 -> RetryLog step fail2 done) -> RetryLog step fail2 done
forall a b. (a -> b) -> a -> b
$ \fail1
failure -> (forall fail2.
 (fail2 -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail2 done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail2 -> Progress step fail2 done) -> Progress step fail2 done)
 -> RetryLog step fail2 done)
-> (forall fail2.
    (fail2 -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail2 done
forall a b. (a -> b) -> a -> b
$ \fail2 -> Progress step fail2 done
g -> fail2 -> Progress step fail2 done
g (fail1 -> fail2
f fail1
failure)

-- | /O(1)/. If the first log leads to failure, continue with the second.
retry :: RetryLog step fail1 done
      -> (fail1 -> RetryLog step fail2 done)
      -> RetryLog step fail2 done
retry :: forall step fail1 done fail2.
RetryLog step fail1 done
-> (fail1 -> RetryLog step fail2 done) -> RetryLog step fail2 done
retry (RetryLog forall fail2.
(fail1 -> Progress step fail2 done) -> Progress step fail2 done
f) fail1 -> RetryLog step fail2 done
g =
    (forall fail2.
 (fail2 -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail2 done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail2 -> Progress step fail2 done) -> Progress step fail2 done)
 -> RetryLog step fail2 done)
-> (forall fail2.
    (fail2 -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail2 done
forall a b. (a -> b) -> a -> b
$ \fail2 -> Progress step fail2 done
extendLog -> (fail1 -> Progress step fail2 done) -> Progress step fail2 done
forall fail2.
(fail1 -> Progress step fail2 done) -> Progress step fail2 done
f ((fail1 -> Progress step fail2 done) -> Progress step fail2 done)
-> (fail1 -> Progress step fail2 done) -> Progress step fail2 done
forall a b. (a -> b) -> a -> b
$ \fail1
failure -> RetryLog step fail2 done
-> forall fail2.
   (fail2 -> Progress step fail2 done) -> Progress step fail2 done
forall step fail done.
RetryLog step fail done
-> forall fail2.
   (fail -> Progress step fail2 done) -> Progress step fail2 done
unRetryLog (fail1 -> RetryLog step fail2 done
g fail1
failure) fail2 -> Progress step fail2 done
extendLog

-- | /O(1)/. Create a log with one message before a failure.
failWith :: step -> fail -> RetryLog step fail done
failWith :: forall step fail done. step -> fail -> RetryLog step fail done
failWith step
m fail
failure = (forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail -> Progress step fail2 done) -> Progress step fail2 done)
 -> RetryLog step fail done)
-> (forall fail2.
    (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall a b. (a -> b) -> a -> b
$ \fail -> Progress step fail2 done
f -> step -> Progress step fail2 done -> Progress step fail2 done
forall step fail done.
step -> Progress step fail done -> Progress step fail done
Step step
m (fail -> Progress step fail2 done
f fail
failure)

-- | /O(1)/. Create a log with one message before a success.
succeedWith :: step -> done -> RetryLog step fail done
succeedWith :: forall step done fail. step -> done -> RetryLog step fail done
succeedWith step
m done
d = (forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail -> Progress step fail2 done) -> Progress step fail2 done)
 -> RetryLog step fail done)
-> (forall fail2.
    (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall a b. (a -> b) -> a -> b
$ Progress step fail2 done
-> (fail -> Progress step fail2 done) -> Progress step fail2 done
forall a b. a -> b -> a
const (Progress step fail2 done
 -> (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> Progress step fail2 done
-> (fail -> Progress step fail2 done)
-> Progress step fail2 done
forall a b. (a -> b) -> a -> b
$ step -> Progress step fail2 done -> Progress step fail2 done
forall step fail done.
step -> Progress step fail done -> Progress step fail done
Step step
m (done -> Progress step fail2 done
forall step fail done. done -> Progress step fail done
Done done
d)

-- | /O(1)/. Prepend a message to a log.
continueWith :: step
             -> RetryLog step fail done
             -> RetryLog step fail done
continueWith :: forall step fail done.
step -> RetryLog step fail done -> RetryLog step fail done
continueWith step
m (RetryLog forall fail2.
(fail -> Progress step fail2 done) -> Progress step fail2 done
f) = (forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail -> Progress step fail2 done) -> Progress step fail2 done)
 -> RetryLog step fail done)
-> (forall fail2.
    (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
forall a b. (a -> b) -> a -> b
$ step -> Progress step fail2 done -> Progress step fail2 done
forall step fail done.
step -> Progress step fail done -> Progress step fail done
Step step
m (Progress step fail2 done -> Progress step fail2 done)
-> ((fail -> Progress step fail2 done) -> Progress step fail2 done)
-> (fail -> Progress step fail2 done)
-> Progress step fail2 done
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (fail -> Progress step fail2 done) -> Progress step fail2 done
forall fail2.
(fail -> Progress step fail2 done) -> Progress step fail2 done
f

-- | /O(1)/. Prepend the given message and 'Enter' to the log, and insert
-- 'Leave' before the failure if the log fails.
tryWith :: Message -> RetryLog Message fail done -> RetryLog Message fail done
tryWith :: forall fail done.
Message -> RetryLog Message fail done -> RetryLog Message fail done
tryWith Message
m RetryLog Message fail done
f =
  (forall fail2.
 (fail -> Progress Message fail2 done)
 -> Progress Message fail2 done)
-> RetryLog Message fail done
forall step fail done.
(forall fail2.
 (fail -> Progress step fail2 done) -> Progress step fail2 done)
-> RetryLog step fail done
RetryLog ((forall fail2.
  (fail -> Progress Message fail2 done)
  -> Progress Message fail2 done)
 -> RetryLog Message fail done)
-> (forall fail2.
    (fail -> Progress Message fail2 done)
    -> Progress Message fail2 done)
-> RetryLog Message fail done
forall a b. (a -> b) -> a -> b
$ Message
-> Progress Message fail2 done -> Progress Message fail2 done
forall step fail done.
step -> Progress step fail done -> Progress step fail done
Step Message
m (Progress Message fail2 done -> Progress Message fail2 done)
-> ((fail -> Progress Message fail2 done)
    -> Progress Message fail2 done)
-> (fail -> Progress Message fail2 done)
-> Progress Message fail2 done
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Message
-> Progress Message fail2 done -> Progress Message fail2 done
forall step fail done.
step -> Progress step fail done -> Progress step fail done
Step Message
Enter (Progress Message fail2 done -> Progress Message fail2 done)
-> ((fail -> Progress Message fail2 done)
    -> Progress Message fail2 done)
-> (fail -> Progress Message fail2 done)
-> Progress Message fail2 done
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RetryLog Message fail done
-> forall fail2.
   (fail -> Progress Message fail2 done)
   -> Progress Message fail2 done
forall step fail done.
RetryLog step fail done
-> forall fail2.
   (fail -> Progress step fail2 done) -> Progress step fail2 done
unRetryLog (RetryLog Message fail done
-> (fail -> RetryLog Message fail done)
-> RetryLog Message fail done
forall step fail1 done fail2.
RetryLog step fail1 done
-> (fail1 -> RetryLog step fail2 done) -> RetryLog step fail2 done
retry RetryLog Message fail done
f (Message -> fail -> RetryLog Message fail done
forall step fail done. step -> fail -> RetryLog step fail done
failWith Message
Leave))