packages feed

serdoc-core-0.1.0.0: src/Data/SerDoc/Info.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoStarIsType #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module Data.SerDoc.Info
where

import Data.Data
import Data.Word
import Language.Haskell.TH.Syntax (Lift)

-- * Documentation annotation types

-- | Used to add descriptions via @ANN@ pragmas. This is necessary because
-- Template Haskell cannot find Haddock comments attached to constructors inside
-- associated types, but it can find annotations on those same constructors.
newtype Description = Description { descriptionParagraphs :: [String] }
  deriving newtype (Show, Read, Eq, Semigroup, Monoid)
  deriving (Data, Typeable, Lift)

-- * 'FieldInfo' and related types

-- | Describes the serialization format of a field.
data FieldInfo codec
  = AnnField !String !(FieldInfo codec)
    -- ^ Adds an annotation to a field.
  | BasicField !BasicFieldInfo
    -- ^ A simple field, with a named type and a size.
  | EnumField !EnumFieldInfo
    -- ^ An enum field, which reports labels and values for the possible values.
  | CompoundField !(CompoundFieldInfo codec)
    -- ^ A field that is composed out of multiple sub-fields, encoded
    -- sequentially.
  | ChoiceField !(ChoiceFieldInfo codec)
    -- ^ A list of alternatives, to be picked based on a given choice condition.
  | ListField !(ListFieldInfo codec)
    -- ^ A list of values, encoded sequentially. The length must be encoded
    -- separately, and can be referenced from a length expression.
  | AliasField !(AliasFieldInfo codec)
    -- ^ Adds an alternative name (alias) to a field type.
  | SumField !(SumFieldInfo codec)
    -- ^ A list of named alternatives. TODO: this is probably redundant and may
    -- not actually work.
  deriving (Show, Eq)

data BasicFieldInfo =
  BasicFieldInfo
    { basicFieldType :: !String
    , basicFieldSize :: !FieldSize
    }
  deriving (Show, Eq)

data EnumFieldInfo =
  EnumFieldInfo
    { enumFieldType :: !String
    , enumFieldSize :: !FieldSize
    , enumFieldValues :: ![(Int, String)]
    }
  deriving (Show, Eq)

data AliasFieldInfo codec =
  AliasFieldInfo
    { aliasFieldName :: !String
    , aliasFieldTarget :: !(FieldInfo codec)
    }
  deriving (Show, Eq)

data CompoundFieldInfo codec =
  CompoundFieldInfo
    { compoundFieldType :: !String
    , compoundFieldSubfields :: ![SubfieldInfo codec]
    }
  deriving (Show, Eq)

data SumFieldInfo codec =
  SumFieldInfo
    { sumFieldType :: !String
    , sumFieldAlternatives :: ![(String, FieldInfo codec)]
    }
  deriving (Show, Eq)

data ListFieldInfo codec =
  ListFieldInfo
    { listSize :: !FieldSize
    , listElemInfo :: !(FieldInfo codec)
    }
  deriving (Show, Eq)

data SubfieldInfo codec =
  SubfieldInfo
    { subfieldName :: !String
    , subfieldInfo :: !(FieldInfo codec)
    }
  deriving (Show, Eq)

data ChoiceCondition
  = IndexField !String
  | IndexFlag !String Word32
  deriving (Show, Eq)

data ChoiceFieldInfo codec =
  ChoiceFieldInfo
    { choiceCondition :: !ChoiceCondition
    , choiceFieldAlternatives :: ![FieldInfo codec]
    }
  deriving (Show, Eq)

annField :: String -> FieldInfo codec -> FieldInfo codec
annField = AnnField

basicField :: String -> FieldSize -> FieldInfo codec
basicField ty size = BasicField $ BasicFieldInfo ty size

enumField :: String -> FieldSize -> [(Int, String)] -> FieldInfo codec
enumField ty size values = EnumField $ EnumFieldInfo ty size values

enumField_ :: String
           -> [String]
           -> FieldInfo codec
enumField_ ty values = enumField ty EnumSize (zip [0,1..] values)

aliasField :: String -> FieldInfo codec -> FieldInfo codec
aliasField name ty = AliasField $ AliasFieldInfo name ty

compoundField :: String -> [(String, FieldInfo codec)] -> FieldInfo codec
compoundField ty subfields =
  CompoundField $
    CompoundFieldInfo
      ty
      [ SubfieldInfo name i
      | (name, i) <- subfields
      ]

choiceField :: ChoiceCondition -> [FieldInfo codec] -> FieldInfo codec
choiceField cond subfields =
  ChoiceField $
    ChoiceFieldInfo
      cond
      subfields

sumField :: String -> [(String, FieldInfo codec)] -> FieldInfo codec
sumField name alternatives =
  SumField $
    SumFieldInfo
      name
      alternatives

listField :: FieldSize -> FieldInfo codec -> FieldInfo codec
listField lengthExpr elemInfo =
  ListField $
    ListFieldInfo
      lengthExpr
      elemInfo

-- * Field sizes

data FieldSize
  = FixedSize !Int -- ^ Exactly this size
  | EnumSize -- ^ The default enum size for the codec
  | VarSize !String -- ^ Size given by a named variable from the context
  | BinopSize !FieldSizeBinop !FieldSize !FieldSize -- ^ Binary operation
  | RangeSize !FieldSize !FieldSize -- ^ Min/max range
  | UnknownSize -- ^ Size is entirely unknown
  deriving (Show, Eq)

knownSize :: FieldSize -> Maybe Int
knownSize (FixedSize i) = Just i
knownSize VarSize {} = Nothing
knownSize (BinopSize FSPlus a b) = (+) <$> knownSize a <*> knownSize b
knownSize (BinopSize FSMul a b) = (*) <$> knownSize a <*> knownSize b
knownSize (BinopSize FSMax a b) = max <$> knownSize a <*> knownSize b
knownSize (BinopSize FSMin a b) = min <$> knownSize a <*> knownSize b
knownSize _ = Nothing

data FieldSizeBinop
  = FSPlus
  | FSMul
  | FSMax
  | FSMin
  deriving (Show, Eq)