{-# LANGUAGE DeriveGeneric #-}

module OpenSuse.Types.Issue
  ( Issue(..), parseIssue, parseCve, parseBsc, showIssue
  , isCve, isBsc
  )
  where

import OpenSuse.Prelude

import Data.Aeson
import Data.List ( isSuffixOf )
import Data.Text ( unpack )
import Text.Read

data Issue = Bsc Natural
           | Cve Natural Natural
  deriving (Int -> Issue -> ShowS
[Issue] -> ShowS
Issue -> String
(Int -> Issue -> ShowS)
-> (Issue -> String) -> ([Issue] -> ShowS) -> Show Issue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Issue] -> ShowS
$cshowList :: [Issue] -> ShowS
show :: Issue -> String
$cshow :: Issue -> String
showsPrec :: Int -> Issue -> ShowS
$cshowsPrec :: Int -> Issue -> ShowS
Show, Issue -> Issue -> Bool
(Issue -> Issue -> Bool) -> (Issue -> Issue -> Bool) -> Eq Issue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Issue -> Issue -> Bool
$c/= :: Issue -> Issue -> Bool
== :: Issue -> Issue -> Bool
$c== :: Issue -> Issue -> Bool
Eq, Eq Issue
Eq Issue
-> (Issue -> Issue -> Ordering)
-> (Issue -> Issue -> Bool)
-> (Issue -> Issue -> Bool)
-> (Issue -> Issue -> Bool)
-> (Issue -> Issue -> Bool)
-> (Issue -> Issue -> Issue)
-> (Issue -> Issue -> Issue)
-> Ord Issue
Issue -> Issue -> Bool
Issue -> Issue -> Ordering
Issue -> Issue -> Issue
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Issue -> Issue -> Issue
$cmin :: Issue -> Issue -> Issue
max :: Issue -> Issue -> Issue
$cmax :: Issue -> Issue -> Issue
>= :: Issue -> Issue -> Bool
$c>= :: Issue -> Issue -> Bool
> :: Issue -> Issue -> Bool
$c> :: Issue -> Issue -> Bool
<= :: Issue -> Issue -> Bool
$c<= :: Issue -> Issue -> Bool
< :: Issue -> Issue -> Bool
$c< :: Issue -> Issue -> Bool
compare :: Issue -> Issue -> Ordering
$ccompare :: Issue -> Issue -> Ordering
$cp1Ord :: Eq Issue
Ord, (forall x. Issue -> Rep Issue x)
-> (forall x. Rep Issue x -> Issue) -> Generic Issue
forall x. Rep Issue x -> Issue
forall x. Issue -> Rep Issue x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Issue x -> Issue
$cfrom :: forall x. Issue -> Rep Issue x
Generic)

instance Hashable Issue
instance Binary Issue
instance NFData Issue

parseIssue :: String -> Issue
parseIssue :: String -> Issue
parseIssue (Char
'C':Char
'V':Char
'E':Char
'-':String
cve) = String -> Issue
parseCve String
cve
parseIssue (Char
'b':Char
's':Char
'c':Char
'#':String
bsc) = String -> Issue
parseBsc String
bsc
parseIssue String
bsc                   = String -> Issue
parseBsc String
bsc

showIssue :: Issue -> String
showIssue :: Issue -> String
showIssue (Cve Natural
y Natural
n) = String
"CVE-" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Natural -> String
forall a. Show a => a -> String
show Natural
y String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"-" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Natural -> String
forall a. Show a => a -> String
show Natural
n
showIssue (Bsc Natural
n) = String
"bsc#" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Natural -> String
forall a. Show a => a -> String
show Natural
n

parseCve :: String -> Issue
parseCve :: String -> Issue
parseCve String
cve = case (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
'-') String
cve of
                 (String
y,Char
'-':String
n) -> Natural -> Natural -> Issue
Cve (String -> String -> Natural
forall a. Read a => String -> String -> a
safeRead String
"cve-year" String
y) (String -> String -> Natural
forall a. Read a => String -> String -> a
safeRead String
"cve-number" String
n)
                 (String, String)
_         -> String -> Issue
forall a. HasCallStack => String -> a
error (String
"malformed CVE: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
cve)

parseBsc :: String -> Issue     -- TODO: https://gitlab.suse.de/l3ms/smelt/issues/184
parseBsc :: String -> Issue
parseBsc String
bsc = Natural -> Issue
Bsc (String -> String -> Natural
forall a. Read a => String -> String -> a
safeRead String
"bsc number" (ShowS
stripFixmeSuffix String
bsc))

instance FromJSON Issue where
  parseJSON :: Value -> Parser Issue
parseJSON = String -> (Text -> Parser Issue) -> Value -> Parser Issue
forall a. String -> (Text -> Parser a) -> Value -> Parser a
withText String
"Issue ID" (Issue -> Parser Issue
forall (m :: * -> *) a. Monad m => a -> m a
return (Issue -> Parser Issue) -> (Text -> Issue) -> Text -> Parser Issue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Issue
parseIssue (String -> Issue) -> (Text -> String) -> Text -> Issue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack)

instance FromJSONKey Issue where
  fromJSONKey :: FromJSONKeyFunction Issue
fromJSONKey = (Text -> Issue) -> FromJSONKeyFunction Issue
forall a. (Text -> a) -> FromJSONKeyFunction a
FromJSONKeyText (String -> Issue
parseIssue (String -> Issue) -> (Text -> String) -> Text -> Issue
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack)

safeRead :: Read a => String -> String -> a
safeRead :: String -> String -> a
safeRead String
ctx String
x = a -> Maybe a -> a
forall a. a -> Maybe a -> a
fromMaybe (String -> a
forall a. HasCallStack => String -> a
error (String
"invalid " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
ctx String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
": " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
x)) (String -> Maybe a
forall a. Read a => String -> Maybe a
readMaybe String
x)

stripFixmeSuffix :: String -> String
stripFixmeSuffix :: ShowS
stripFixmeSuffix String
x
  | String
"_FIXME" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
x = ShowS
forall a. [a] -> [a]
reverse ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> ShowS
forall a. Int -> [a] -> [a]
drop Int
6 ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
forall a. [a] -> [a]
reverse ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$ String
x
  | Bool
otherwise               = String
x

isCve :: Issue -> Bool
isCve :: Issue -> Bool
isCve (Cve Natural
_ Natural
_) = Bool
True
isCve Issue
_ = Bool
False

isBsc :: Issue -> Bool
isBsc :: Issue -> Bool
isBsc (Bsc Natural
_) = Bool
True
isBsc Issue
_ = Bool
False