content: -- | Simple value types and functions.
  module Analyze.Values where
  
  import           Analyze.Common      (Data)
  import           Control.Monad.Catch (Exception, MonadThrow (..))
  import           Data.Text           (Text)
  import           Data.Typeable       (Typeable)
  
  -- | Singleton type for value types.
  data ValueType =
      ValueTypeText
    | ValueTypeInteger
    | ValueTypeDouble
    | ValueTypeBool
    deriving (Show, Eq, Enum, Bounded)
  
  -- | Union type for values.
  data Value =
      ValueText Text
    | ValueInteger Integer
    | ValueDouble Double
    | ValueBool Bool
    deriving (Show, Eq)
  
  -- | Returns the type of the value.
  valueToType :: Value -> ValueType
  valueToType (ValueText _)    = ValueTypeText
  valueToType (ValueInteger _) = ValueTypeInteger
  valueToType (ValueDouble _)  = ValueTypeDouble
  valueToType (ValueBool _)    = ValueTypeBool
  
  -- | Extracts 'Text' from the 'Value'.
  getText :: Value -> Maybe Text
  getText (ValueText s) = Just s
  getText _             = Nothing
  
  -- | Extracts 'Integer' from the 'Value'.
  getInteger :: Value -> Maybe Integer
  getInteger (ValueInteger i) = Just i
  getInteger _                = Nothing
  
  -- | Extracts 'Double' from the 'Value'.
  getDouble :: Value -> Maybe Double
  getDouble (ValueDouble d) = Just d
  getDouble _               = Nothing
  
  -- | Extracts 'Bool' from the 'Value'.
  getBool :: Value -> Maybe Bool
  getBool (ValueBool b) = Just b
  getBool _             = Nothing
  
  -- | Exception for when we encounder unexpected values.
  data ValueTypeError k = ValueTypeError k ValueType Value deriving (Show, Eq, Typeable)
  instance (Show k, Typeable k) => Exception (ValueTypeError k)
  
  -- | Use with 'Analyze.Decoding.requireWhere' to read 'Text' values.
  textual :: (Data k, MonadThrow m) => k -> Value -> m Text
  textual _ (ValueText s) = pure s
  textual k v             = throwM (ValueTypeError k ValueTypeText v)
  
  -- | Use with 'Analyze.Decoding.requireWhere' to read 'Integer' values.
  integral :: (Data k, MonadThrow m) => k -> Value -> m Integer
  integral _ (ValueInteger s) = pure s
  integral k v                = throwM (ValueTypeError k ValueTypeInteger v)
  
  -- | Use with 'Analyze.Decoding.requireWhere' to read 'Double' values.
  floating :: (Data k, MonadThrow m) => k -> Value -> m Double
  floating _ (ValueDouble s) = pure s
  floating k v               = throwM (ValueTypeError k ValueTypeDouble v)
  
  -- | Use with 'Analyze.Decoding.requireWhere' to read 'Bool' values.
  boolean :: (Data k, MonadThrow m) => k -> Value -> m Bool
  boolean _ (ValueBool s) = pure s
  boolean k v             = throwM (ValueTypeError k ValueTypeBool v)
  