{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Data.Result
  ( Result
  , get, errors
  , raise
  ) where


import Data.Bifunctor
import Data.Bifoldable
import Data.Bitraversable
import Data.Key
import qualified Data.List.NonEmpty as NonEmpty
import Data.Monoid


newtype Result e a =
    Result (Either (NonEmpty.NonEmpty e) a)
  deriving (Eq, Ord, Show, Functor, Applicative, Monad, Foldable)


instance Traversable (Result e) where
  traverse f (Result e) =
    case e of
      Left es ->
        pure (Result (Left es))
      Right a ->
        Result . Right <$> f a


instance Zip (Result e) where
  zipWith f mea meb =
    case (mea, meb) of
      (Result (Right eaa), Result (Right eba)) ->
        Result (Right (f eaa eba))
      _ ->
        Result (Left (NonEmpty.fromList (errors mea <> errors meb)))


instance Bifunctor Result where
  bimap f g (Result e) =
    case e of
      Left es ->
        Result (Left (fmap f es))
      Right a ->
        Result (Right (g a))


instance Bifoldable Result where
  bifoldMap f g (Result e) =
    case e of
      Left es ->
        foldMap f es
      Right a ->
        g a


instance Bitraversable Result where
  bitraverse f g (Result e) =
    case e of
      Left es ->
        Result . Left <$> traverse f es
      Right a ->
        Result . Right <$> g a


raise :: e -> Result e a
raise e =
  Result (Left (e NonEmpty.:| []))


get :: Result e a -> Maybe a
get (Result e) =
  case e of
    Left _ ->
      Nothing
    Right a ->
      Just a


errors :: Result e a -> [e]
errors (Result e) =
  case e of
    Left es ->
      NonEmpty.toList es
    Right _ ->
      []