{-# LANGUAGE DeriveFoldable #-}
module Textual.SepList
    (
     SepList,
     sepList,
     slItems
    ) where

import Lawless
import Data.Textual
import Text.Printer (hsep)
import Data.Binary
import Data.List.NonEmpty
import Data.Foldable (Foldable)
import Data.Binary.Get
import Control.Monad.Fail

type SepList a = SepList' (NonEmpty a)

sepList   a. (Ord a, Eq a, Binary a, Printable a)  a  SepList a
sepList a = SepList' $ a :| []

-- | A serializable printable separated list.
data SepList' a where
  SepList'   a. (Ord a, Eq a, Binary a, Printable a)  NonEmpty a  SepList' (NonEmpty a)

deriving instance Foldable SepList'
deriving instance Eq (SepList a)
deriving instance Ord (SepList a)

slItems  Lens' (SepList' (NonEmpty a)) (NonEmpty a)
slItems = lens (\(SepList' i)  i) (\(SepList' _) j  SepList' j)

instance (Ord a)  Semigroup (SepList' (NonEmpty a)) where
  a <> b = a & slItems .~ ((a ^. slItems) <> (b ^. slItems))

instance (Ord a, Binary a, Printable a)  Binary (SepList' (NonEmpty a)) where
  put = putList  toList  view slItems
  get = label "SepList'" $ do
    (nonEmpty <$> get) >>= \case
      Nothing  fail "Encountered an empty SepList'"
      Just l  return  SepList' $ l

instance (Printable a)  Printable (SepList' (NonEmpty a)) where
  print = hsep  over traversed print  view slItems

instance (Printable a) Show (SepList a) where
  show = toString