module Must (Must (..), inRange, exceedsUpperBound, validateMust) where
import Data.Char (isDigit)
import Data.List (isInfixOf)
import Text.Printf (printf)
import Text.Read (readMaybe)
data Must
= MtDisabled
| MtExact Int
| MtRange (Maybe Int) (Maybe Int)
deriving (Must -> Must -> Bool
(Must -> Must -> Bool) -> (Must -> Must -> Bool) -> Eq Must
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Must -> Must -> Bool
== :: Must -> Must -> Bool
$c/= :: Must -> Must -> Bool
/= :: Must -> Must -> Bool
Eq)
instance Show Must where
show :: Must -> String
show Must
MtDisabled = String
"0"
show (MtExact Int
n) = Int -> String
forall a. Show a => a -> String
show Int
n
show (MtRange Maybe Int
Nothing Maybe Int
Nothing) = String
".."
show (MtRange Maybe Int
Nothing (Just Int
hi)) = String
".." String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
hi
show (MtRange (Just Int
lo) Maybe Int
Nothing) = Int -> String
forall a. Show a => a -> String
show Int
lo String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
".."
show (MtRange (Just Int
lo) (Just Int
hi)) = Int -> String
forall a. Show a => a -> String
show Int
lo String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
".." String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
hi
instance Read Must where
readsPrec :: Int -> ReadS Must
readsPrec Int
_ String
"0" = [(Must
MtDisabled, String
"")]
readsPrec Int
_ String
s
| String
".." String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
s = ReadS Must
parseRange String
s
| Bool
otherwise = ReadS Must
parseExact String
s
where
readInt :: String -> Maybe Int
readInt :: String -> Maybe Int
readInt String
str
| String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
str Bool -> Bool -> Bool
|| Bool -> Bool
not ((Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
str) = Maybe Int
forall a. Maybe a
Nothing
| Bool
otherwise = do
Integer
num <- String -> Maybe Integer
forall a. Read a => String -> Maybe a
readMaybe String
str :: Maybe Integer
if Integer
num Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Int -> Integer
forall a. Integral a => a -> Integer
toInteger (Int
forall a. Bounded a => a
maxBound :: Int) Bool -> Bool -> Bool
|| Integer
num Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Int -> Integer
forall a. Integral a => a -> Integer
toInteger (Int
forall a. Bounded a => a
minBound :: Int)
then Maybe Int
forall a. Maybe a
Nothing
else Int -> Maybe Int
forall a. a -> Maybe a
Just (Integer -> Int
forall a. Num a => Integer -> a
fromInteger Integer
num)
parseRange :: String -> [(Must, String)]
parseRange :: ReadS Must
parseRange String
str = 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
str of
(String
loStr, Char
'.' : Char
'.' : String
hiStr) ->
let loPart :: Maybe Int
loPart = if String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
loStr then Maybe Int
forall a. Maybe a
Nothing else String -> Maybe Int
readInt String
loStr
hiPart :: Maybe Int
hiPart = if String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
hiStr then Maybe Int
forall a. Maybe a
Nothing else String -> Maybe Int
readInt String
hiStr
in case (Maybe Int
loPart, Maybe Int
hiPart, String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
loStr, String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
hiStr) of
(Maybe Int
Nothing, Maybe Int
Nothing, Bool
False, Bool
False) -> []
(Maybe Int
Nothing, Maybe Int
Nothing, Bool
True, Bool
True) -> [(Maybe Int -> Maybe Int -> Must
MtRange Maybe Int
forall a. Maybe a
Nothing Maybe Int
forall a. Maybe a
Nothing, String
"")]
(Maybe Int
Nothing, Just Int
hi, Bool
True, Bool
False) ->
[(Maybe Int -> Maybe Int -> Must
MtRange Maybe Int
forall a. Maybe a
Nothing (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
hi), String
"") | Int
hi Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0]
(Just Int
lo, Maybe Int
Nothing, Bool
False, Bool
True) ->
[(Maybe Int -> Maybe Int -> Must
MtRange (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
lo) Maybe Int
forall a. Maybe a
Nothing, String
"") | Int
lo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0]
(Just Int
lo, Just Int
hi, Bool
False, Bool
False) ->
[(Maybe Int -> Maybe Int -> Must
MtRange (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
lo) (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
hi), String
"") | Int
lo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
hi Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Int
lo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
hi]
(Maybe Int, Maybe Int, Bool, Bool)
_ -> []
(String, String)
_ -> []
parseExact :: String -> [(Must, String)]
parseExact :: ReadS Must
parseExact String
str = case String -> Maybe Int
readInt String
str of
Just Int
n | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 -> [(if Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Must
MtDisabled else Int -> Must
MtExact Int
n, String
"")]
Just Int
_ -> []
Maybe Int
Nothing -> []
inRange :: Must -> Int -> Bool
inRange :: Must -> Int -> Bool
inRange Must
MtDisabled Int
_ = Bool
True
inRange (MtExact Int
expected) Int
actual = Int
actual Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
expected
inRange (MtRange Maybe Int
minVal Maybe Int
maxVal) Int
actual =
Bool
checkMin Bool -> Bool -> Bool
&& Bool
checkMax
where
checkMin :: Bool
checkMin = Bool -> (Int -> Bool) -> Maybe Int -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
True (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
actual) Maybe Int
minVal
checkMax :: Bool
checkMax = Bool -> (Int -> Bool) -> Maybe Int -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
True (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
actual) Maybe Int
maxVal
exceedsUpperBound :: Must -> Int -> Bool
exceedsUpperBound :: Must -> Int -> Bool
exceedsUpperBound Must
MtDisabled Int
_ = Bool
False
exceedsUpperBound (MtExact Int
n) Int
current = Int
current Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
n
exceedsUpperBound (MtRange Maybe Int
_ (Just Int
hi)) Int
current = Int
current Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
hi
exceedsUpperBound (MtRange Maybe Int
_ Maybe Int
Nothing) Int
_ = Bool
False
validateMust :: Must -> Maybe String
validateMust :: Must -> Maybe String
validateMust Must
MtDisabled = Maybe String
forall a. Maybe a
Nothing
validateMust (MtExact Int
n) | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = String -> Maybe String
forall a. a -> Maybe a
Just String
"--must exact value must be positive"
validateMust (MtRange (Just Int
lo) Maybe Int
_) | Int
lo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Maybe String
forall a. a -> Maybe a
Just String
"--must minimum must be non-negative"
validateMust (MtRange Maybe Int
_ (Just Int
hi)) | Int
hi Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Maybe String
forall a. a -> Maybe a
Just String
"--must maximum must be non-negative"
validateMust (MtRange Maybe Int
Nothing Maybe Int
_) = Maybe String
forall a. Maybe a
Nothing
validateMust (MtRange Maybe Int
_ Maybe Int
Nothing) = Maybe String
forall a. Maybe a
Nothing
validateMust (MtRange (Just Int
lo) (Just Int
hi))
| Int
lo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
hi = String -> Maybe String
forall a. a -> Maybe a
Just (String -> Int -> Int -> String
forall r. PrintfType r => String -> r
printf String
"--must range invalid: minimum (%d) is greater than maximum (%d)" Int
lo Int
hi)
| Bool
otherwise = Maybe String
forall a. Maybe a
Nothing
validateMust Must
_ = Maybe String
forall a. Maybe a
Nothing