module Unison.Merge.TwoDiffOps
  ( TwoDiffOps (..),
    make,
  )
where

import Data.These (These (..))
import Unison.Merge.DiffOp (DiffOp (..))
import Unison.Merge.EitherWay (EitherWay (..))
import Unison.Merge.TwoWay (TwoWay (..))
import Unison.Merge.Updated (Updated (..))

data TwoDiffOps a
  = TwoDiffOps'Add !(EitherWay a)
  | TwoDiffOps'Delete !(EitherWay a)
  | TwoDiffOps'Update !(EitherWay (Updated a))
  | TwoDiffOps'AddAdd !(TwoWay a)
  | TwoDiffOps'DeleteDelete !a
  | TwoDiffOps'DeleteUpdate !(Updated a) -- old was deleted by Alice
  | TwoDiffOps'UpdateDelete !(Updated a) -- old was deleted by Bob
  | TwoDiffOps'UpdateUpdate !a !(TwoWay a) -- old, new

-- | Combine two aligned @DiffOp@ into one @TwoDiffOps@.
make :: These (DiffOp a) (DiffOp a) -> TwoDiffOps a
make :: forall a. These (DiffOp a) (DiffOp a) -> TwoDiffOps a
make = \case
  This DiffOp a
alice -> (forall a. a -> EitherWay a) -> DiffOp a -> TwoDiffOps a
forall a. (forall a. a -> EitherWay a) -> DiffOp a -> TwoDiffOps a
one a -> EitherWay a
forall a. a -> EitherWay a
Alice DiffOp a
alice
  That DiffOp a
bob -> (forall a. a -> EitherWay a) -> DiffOp a -> TwoDiffOps a
forall a. (forall a. a -> EitherWay a) -> DiffOp a -> TwoDiffOps a
one a -> EitherWay a
forall a. a -> EitherWay a
Bob DiffOp a
bob
  These (DiffOp'Add a
alice) (DiffOp'Add a
bob) -> TwoWay a -> TwoDiffOps a
forall a. TwoWay a -> TwoDiffOps a
TwoDiffOps'AddAdd TwoWay {a
alice :: a
$sel:alice:TwoWay :: a
alice, a
bob :: a
$sel:bob:TwoWay :: a
bob}
  These (DiffOp'Delete a
alice) (DiffOp'Delete a
_) -> a -> TwoDiffOps a
forall a. a -> TwoDiffOps a
TwoDiffOps'DeleteDelete a
alice
  These (DiffOp'Delete a
_) (DiffOp'Update Updated a
bob) -> Updated a -> TwoDiffOps a
forall a. Updated a -> TwoDiffOps a
TwoDiffOps'DeleteUpdate Updated a
bob
  These (DiffOp'Update Updated a
alice) (DiffOp'Delete a
_) -> Updated a -> TwoDiffOps a
forall a. Updated a -> TwoDiffOps a
TwoDiffOps'UpdateDelete Updated a
alice
  These (DiffOp'Update Updated a
alice) (DiffOp'Update Updated a
bob) ->
    a -> TwoWay a -> TwoDiffOps a
forall a. a -> TwoWay a -> TwoDiffOps a
TwoDiffOps'UpdateUpdate Updated a
alice.old TwoWay {$sel:alice:TwoWay :: a
alice = Updated a
alice.new, $sel:bob:TwoWay :: a
bob = Updated a
bob.new}
  -- These don't make sense - e.g. someone can't update or delete something that wasn't there
  These (DiffOp'Add a
_) (DiffOp'Delete a
_) -> [Char] -> TwoDiffOps a
forall a. HasCallStack => [Char] -> a
error [Char]
"impossible"
  These (DiffOp'Add a
_) (DiffOp'Update Updated a
_) -> [Char] -> TwoDiffOps a
forall a. HasCallStack => [Char] -> a
error [Char]
"impossible"
  These (DiffOp'Delete a
_) (DiffOp'Add a
_) -> [Char] -> TwoDiffOps a
forall a. HasCallStack => [Char] -> a
error [Char]
"impossible"
  These (DiffOp'Update Updated a
_) (DiffOp'Add a
_) -> [Char] -> TwoDiffOps a
forall a. HasCallStack => [Char] -> a
error [Char]
"impossible"
  where
    one :: (forall a. a -> EitherWay a) -> DiffOp a -> TwoDiffOps a
    one :: forall a. (forall a. a -> EitherWay a) -> DiffOp a -> TwoDiffOps a
one forall a. a -> EitherWay a
who = \case
      DiffOp'Add a
x -> EitherWay a -> TwoDiffOps a
forall a. EitherWay a -> TwoDiffOps a
TwoDiffOps'Add (a -> EitherWay a
forall a. a -> EitherWay a
who a
x)
      DiffOp'Delete a
x -> EitherWay a -> TwoDiffOps a
forall a. EitherWay a -> TwoDiffOps a
TwoDiffOps'Delete (a -> EitherWay a
forall a. a -> EitherWay a
who a
x)
      DiffOp'Update Updated a
x -> EitherWay (Updated a) -> TwoDiffOps a
forall a. EitherWay (Updated a) -> TwoDiffOps a
TwoDiffOps'Update (Updated a -> EitherWay (Updated a)
forall a. a -> EitherWay a
who Updated a
x)