{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -- | module defining Badge types and utility functions module Badges where import Data.Aeson (Options (fieldLabelModifier, sumEncoding), SumEncoding (UntaggedValue), ToJSON (toJSON), defaultOptions, genericToJSON, (.=)) import qualified Data.Aeson as A import Data.Char (toLower) import Data.Text (Text) import GHC.Generics (Generic) import Text.Regex.TDFA ((=~)) data BadgeArea = BadgePoint { areaX :: Double , areaY :: Double } | BadgeRect { areaX :: Double , areaY :: Double , areaWidth :: Double , areaHeight :: Double , areaIsEllipse :: Bool } deriving (Ord, Eq, Generic, Show) newtype BadgeToken = BadgeToken Text deriving (Eq, Ord, Show) instance ToJSON BadgeArea where toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 4 . map toLower , sumEncoding = UntaggedValue } instance ToJSON BadgeToken where toJSON (BadgeToken text) = toJSON text parseToken :: Text -> Maybe BadgeToken parseToken text = if text =~ ("^[a-zA-Z0-9]{50}$" :: Text) -- TODO: add character limit then Just (BadgeToken text) else Nothing data Badge = Badge BadgeToken BadgeArea deriving (Ord, Eq, Generic, Show) badgeJsonArray :: A.KeyValue a => Badge -> [a] badgeJsonArray (Badge token area) = [ "token" .= token ] <> areaObject where areaObject = case area of BadgePoint x y -> [ "x" .= x , "y" .= y , "type" .= A.String "point" ] BadgeRect {..} -> [ "x" .= areaX , "y" .= areaY , "width" .= areaWidth , "height" .= areaHeight , "type" .= if areaIsEllipse then A.String "ellipse" else A.String "rectangle" ] badgeJson :: FilePath -> Badge -> A.Value badgeJson mappath badge = A.object (badgeJsonArray badge <> [ "map" .= mappath ])