module Hydrogen.Data ( Value , MkValue (..) , mkLink , readValue , isInteger , isNumber , isVersion , isUUID , isDateTime , isDate , isTime , isLink , isNull , isNotANumber , isPositive , isZero , isNegative , isNonNegative , getNumber , getVersion , getUUID , getDateTime , getDate , getTime , getLink , getInteger ) where import Hydrogen.Prelude hiding (isNumber) import Hydrogen.Parsing hiding (parse) import qualified Data.Version data Value where DNumber :: Rational -> Value DVersion :: Version -> Value DUUID :: UUID -> Value DDateTime :: ZonedTime -> Value DDate :: Day -> Value DTime :: TimeOfDay -> Value DLink :: String -> Value DNull :: Value DNotANumber :: Value deriving (Eq, Generic, Typeable, Show, Read) instance Serialize Value readValue :: String -> Maybe Value readValue = firstJust [ fmap DNumber . tryReadDecimal , fmap DDateTime . join . tryReadDateTime , fmap DDate . join . tryReadDate , fmap DTime . join . tryReadTime , fmap DLink . tryReadLink , fmap DVersion . tryReadVersion , fmap DUUID . tryReadUUID ] class MkValue a where mkValue :: a -> Value instance MkValue Int where mkValue = DNumber . fromIntegral instance MkValue Integer where mkValue = DNumber . fromIntegral instance MkValue Double where mkValue d | isNaN d = DNotANumber | otherwise = DNumber $ toRational d instance MkValue Float where mkValue d | isNaN d = DNotANumber | otherwise = DNumber $ toRational d instance MkValue (Ratio Integer) where mkValue = DNumber instance MkValue ZonedTime where mkValue = DDateTime instance MkValue TimeOfDay where mkValue = DTime instance MkValue Day where mkValue = DDate instance MkValue Version where mkValue = DVersion instance MkValue Data.Version.Version where mkValue = DVersion . fromDataVersion instance MkValue () where mkValue _ = DNull instance (MkValue a, MkValue b) => MkValue (Either a b) where mkValue = either mkValue mkValue instance MkValue a => MkValue (Maybe a) where mkValue = maybe DNull mkValue mkLink :: String -> Maybe Value mkLink = fmap DLink . tryReadLink isNumber, isVersion, isUUID, isDateTime, isDate, isTime, isLink, isInteger, isNull, isNotANumber, isPositive, isZero, isNegative, isNonNegative :: Value -> Bool isPositive = \case DNumber r -> numerator r > 0 _ -> False isZero = \case DNumber r -> numerator r == 0 _ -> False isNegative = \case DNumber r -> numerator r < 0 _ -> False isNonNegative = not . isNegative isInteger = \case DNumber r -> denominator r == 1 _ -> False isNumber = \case DNumber _ -> True _ -> False isVersion = \case DVersion _ -> True _ -> False isUUID = \case DUUID _ -> True _ -> False isDateTime = \case DDateTime _ -> True _ -> False isDate = \case DDate _ -> True _ -> False isTime = \case DTime _ -> True _ -> False isLink = \case DLink _ -> True _ -> False isNull = \case DNull -> True _ -> False isNotANumber = \case DNotANumber -> True _ -> False getInteger :: Monad m => Value -> m Integer getInteger = \case DNumber r | denominator r == 1 -> return (numerator r) _ -> fail "not an Integer" getNumber :: Monad m => Value -> m Rational getNumber = \case DNumber r -> return r _ -> fail "not a Number" getVersion :: Monad m => Value -> m Version getVersion = \case DVersion v -> return v _ -> fail "not a Version" getUUID :: Monad m => Value -> m UUID getUUID = \case DUUID u -> return u _ -> fail "not a UUID" getDateTime :: Monad m => Value -> m ZonedTime getDateTime = \case DDateTime d -> return d _ -> fail "not a DateTime" getDate :: Monad m => Value -> m Day getDate = \case DDate d -> return d _ -> fail "not a Date" getTime :: Monad m => Value -> m TimeOfDay getTime = \case DTime d -> return d _ -> fail "not a Time" getLink :: Monad m => Value -> m String getLink = \case DLink l -> return l _ -> fail "not a Link"