packages feed

binrep-1.1.0: src/Binrep/Type/Prefix/Count.hs

{-# LANGUAGE UndecidableInstances #-} -- required for type-level stuff
{-# LANGUAGE OverloadedStrings #-} -- required for refined errors

module Binrep.Type.Prefix.Count where

import Binrep.Type.Prefix.Internal
import Binrep
import Control.Monad.Combinators qualified as Monad

import GHC.TypeNats
import Util.TypeNats ( natValInt )

import Rerefined.Predicate.Common
import Rerefined.Refine
import TypeLevelShow.Utils

import Data.Kind ( type Type )

import Data.Foldable qualified as Foldable

-- TODO put monofoldable in here, instead of that useless @(f a)@ stuff

data CountPrefix (pfx :: Type)

instance Predicate (CountPrefix pfx) where
    type PredicateName d (CountPrefix pfx) = ShowParen (d > 9)
        ("CountPrefix " ++ LenNatName pfx)

instance
  ( KnownPredicateName (CountPrefix pfx), KnownNat (LenNatMax pfx), Foldable f
  ) => Refine1 (CountPrefix pfx) f where
    validate1 p fa =
        validateBool p (Foldable.length fa <= natValInt @(LenNatMax pfx)) $
            "TODO too large for count prefix"

instance
  ( KnownPredicateName (CountPrefix pfx), KnownNat (LenNatMax pfx), Foldable f
  ) => Refine (CountPrefix pfx) (f a) where
    validate = validate1

type CountPrefixed pfx = Refined1 (CountPrefix pfx)

-- | We can know byte length at compile time /if/ we know it for the prefix and
--   the list-like.
--
-- This is extremely unlikely, because then what counting are we even
-- performing for the list-like? But it's a valid instance.
instance IsCBLen (CountPrefixed pfx f a) where
    type CBLen (CountPrefixed pfx f a) = CBLen pfx + CBLen (f a)

-- | The byte length of a count-prefixed type is the length of the prefix type
--   (holding the length of the type) plus the length of the type.
--
-- Bit confusing. How to explain this? TODO
instance (LenNat pfx, Foldable f, BLen pfx, BLen (f a))
  => BLen (CountPrefixed pfx f a) where
    blen rfa = blen (lenToNat @pfx (Foldable.length fa)) + blen fa
      where fa = unrefine1 rfa

instance (LenNat pfx, Foldable f, Put pfx, Put (f a))
  => Put (CountPrefixed pfx f a) where
    put rfa = put (lenToNat @pfx (Foldable.length fa)) <> put fa
      where fa = unrefine1 rfa

class GetCount f where getCount :: Get a => Int -> Getter (f a)
instance GetCount [] where getCount n = Monad.count n get

instance (LenNat pfx, GetCount f, Get pfx, Get a)
  => Get (CountPrefixed pfx f a) where
    get = do
        pfx <- get @pfx
        fa <- getCount (natToLen pfx)
        pure $ unsafeRefine1 fa