{-# LANGUAGE OverloadedStrings #-}

module Data.Cve
  ( Cve(..)
  , encode
  , decode
  ) where

import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Read as TR
import qualified Data.Aeson as AE

data Cve = Cve
  { cveYear :: {-# UNPACK #-} !Word
  , cveSequence :: {-# UNPACK #-} !Word
  } deriving (Show,Read,Eq,Ord)

instance AE.ToJSON Cve where
  toJSON = AE.String . encode

instance AE.FromJSON Cve where
  parseJSON = AE.withText "Cve" $ \t -> case decode t of
    Nothing -> fail "could not decode CVE"
    Just c -> return c

encode :: Cve -> Text
encode (Cve y s) = T.concat ["CVE-",T.pack (show y),"-",padding,T.pack (show s)]
  where
  padding
    | s < 10 = "000"
    | s < 100 = "00"
    | s < 1000 = "0"
    | otherwise = T.empty

decode :: Text -> Maybe Cve
decode t = case T.splitOn (T.singleton '-') t of
  [a,b,c] -> if a == "CVE" || a == "cve" || a == "!CVE" || a == "!cve"
    then Nothing
    else decodeDigitParts b c
  [b,c] -> decodeDigitParts b c
  _ -> Nothing

decodeDigitParts :: Text -> Text -> Maybe Cve
decodeDigitParts yt st = case TR.decimal yt of
  Left _ -> Nothing
  Right (y,yx) -> if T.null yx
    then case TR.decimal st of
      Left _ -> Nothing
      Right (s,sx) -> if T.null sx
        then Just (Cve y s)
        else Nothing
    else Nothing