> {-| > Defines a homogenous collection of field types to interface typeless > HTTP and HTML with the application and with HaskellDB > -} > module Frame.Types ( > module Database.HaskellDB.FieldType, > FieldName, > WrapperType (..), > Fields, > showField, > purge, > Wrappable (..), > wrapInt, > wrapBool, > wrapError, > isMandatory, > maybeUnwrap, > fromList', > unwrapField > ) where > import Prelude hiding (lookup) > import Data.Map hiding (map) > import Database.HaskellDB.BoundedList > import Database.HaskellDB.DBLayout > import Database.HaskellDB.FieldType > import Data.Binary > import Data.Maybe > import Control.Monad > import Frame.Utilities > -- | Label for a specific field > type FieldName = String > -- | Collection of heterogenous fields associated by 'FieldName' > type Fields = Map FieldName WrapperType > -- | Casts the 'FieldName' in fields to string > showField :: FieldName -- ^ Field name to look up > -> Fields -- ^ Fields to look in > -> String -- ^ String representation of field to return > showField fn fs = case lookup fn fs of > Nothing -> "" > Just w -> show w > purge' :: DBInfo > -> FieldName > -> WrapperType > -> Bool > purge' _ fn (WrapEmpty (BStrT _)) = True > purge' db fn (WrapEmpty _) = case isMandatory db fn of > (Just b) -> not b > Nothing -> True > purge' db _ _ = True > -- | Purge non mandatory empty fields > purge :: DBInfo > -> Fields -- ^ Fields to purge > -> Fields -- ^ Purged fields > purge db = filterWithKey (purge' db) > lookupFD' :: DBInfo -> String -> String -> Maybe FieldDesc > lookupFD' DBInfo {tbls = []} t _ = Nothing > lookupFD' d@DBInfo {tbls = (TInfo {tname = n, cols = cs}:ts)} t c = if n == t then lookupFD'' cs c else lookupFD' d {tbls = ts} t c > lookupFD'' :: [CInfo] > -> String > -> Maybe FieldDesc > lookupFD'' [] c = Nothing > lookupFD'' (CInfo {cname=n, descr=d}:cs) c = if n == c then Just d else lookupFD'' cs c > -- | Find a FieldDesc(ription) of a particular FieldName > lookupFD :: DBInfo -- ^ Database description being searched > -> FieldName -- ^ Field name being looked up > -> Maybe FieldDesc -- ^ Description of field found > lookupFD db fn = let (l, fns) = explodeFieldName fn in > if l /= 2 then Nothing else > lookupFD' db (head fns) (head $ tail fns) > lookupT :: DBInfo > -> FieldName > -> Maybe FieldType > lookupT db fn = liftM fst $ lookupFD db fn > lookupM :: DBInfo > -> FieldName > -> Maybe Bool > lookupM db fn = liftM snd $ lookupFD db fn > -- | Heterogeneous type wrapper > data WrapperType > = WrapString (Maybe Int) String -- ^ String wrapper > | WrapInt Int -- ^ Int wrappr > | WrapBool Bool -- ^ Bool wrapper > | WrapError FieldType String -- ^ Type error (specific case of error) > | WrapEmpty FieldType -- ^ Empty type > instance Show WrapperType where > show (WrapString _ s) = s > show (WrapInt i) = show i > show (WrapBool b) = show b > show (WrapError _ s) = s > show (WrapEmpty _) = "" > isMandatory :: DBInfo -> FieldName -> Maybe Bool > isMandatory = lookupM > class Wrappable a where > -- | Function to wrap a value associated with a given 'FieldName' > wrap :: DBInfo -- ^ The database to check for the type > -> FieldName -- ^ Field name that represents this value > -> a -- ^ The value in question > -> WrapperType -- ^ The wrapped value > > -- | Function to unwrap a 'WrapperType' to its original type > unwrap :: WrapperType -- ^ The wrapped type > -> a -- ^ The originally typed value > instance Wrappable Int where > wrap db fn i = wrap db fn (show i) > unwrap (WrapInt i) = i > unwrap (WrapEmpty _) = 0 > unwrap (WrapString _ s) = read s > unwrap (WrapBool False) = 0 > unwrap (WrapBool True) = 1 > unwrap _ = error "Not an Int" > instance Wrappable WrapperType where > wrap _ _ w = w > unwrap w = w > instance Wrappable [Char] where > wrap db fn s = let t = lookupT db fn in > wrap' s t > unwrap (WrapString _ s) = s > unwrap (WrapEmpty _) = "" > unwrap (WrapInt i) = show i > unwrap (WrapBool b) = show b > unwrap _ = "Error in unwrapping potential String" > instance Wrappable Bool where > wrap db fn b = wrap db fn (show b) > unwrap (WrapBool b) = b > unwrap (WrapEmpty b) = False > unwrap (WrapString _ "False") = False > unwrap (WrapString _ "True") = True > unwrap (WrapInt 0) = False > unwrap (WrapInt 1) = True > unwrap _ = error "Not an Bool" > instance Size n => Wrappable (BoundedList Char n) where > wrap db fn bs = wrap db fn $ fromBounded bs > unwrap w = trunc $ unwrap w > instance Show a => Wrappable (Maybe a) where > wrap db fn (Just a) = wrap db fn $ show a > wrap _ _ Nothing = WrapEmpty IntT > unwrap _ = Nothing > -- | Wrap a 'String' representation of an 'Int' > wrapInt :: String -- 'String' to be wrapped > -> WrapperType -- The wrapped 'Int' (unless empty/error) > wrapInt v = wrapInt' v $ reads v > wrapInt' :: String > -> [(Int, String)] > -> WrapperType > wrapInt' _ ((v,""):ts) = WrapInt v > wrapInt' "" _ = WrapEmpty IntT > wrapInt' v ((_,_):ts) = WrapError IntT v > wrapInt' v [] = WrapError IntT v > -- | Wrap a 'String' representation of a 'Bool' > wrapBool :: String -- 'String' to be wrapped > -> WrapperType -- The wrapped 'Bool' (unless empty/error) > wrapBool v = wrapBool' v $ reads v > wrapBool' :: String > -> [(Bool, String)] > -> WrapperType > wrapBool' _ ((v,""):ts) = WrapBool v > wrapBool' "" _ = WrapEmpty BoolT > wrapBool' v ((_,_):ts) = WrapError BoolT v > wrapBool' v [] = WrapError BoolT v > -- | Returns an error message if there has been a wrapping error > wrapError :: WrapperType -- ^ The wrapped type to check > -> String -- ^ Error message > wrapError (WrapError IntT _) = "Needs to be a number" > wrapError (WrapError BoolT _) = "Needs to be a boolean" > wrapError (WrapError (BStrT _) _) = "Could not wrap" > wrap' :: String > -> Maybe FieldType > -> WrapperType > wrap' v (Just IntT) = wrapInt v > wrap' v (Just (BStrT l)) = WrapString (Just l) v > wrap' v (Just BoolT) = wrapBool v > wrap' "" (Just t) = WrapEmpty t > wrap' "" Nothing = WrapEmpty $ BStrT 0 > wrap' v (Just t) = WrapError t v > wrap' v Nothing = WrapError (BStrT $ length v) v > -- | A potential 'WrapperType' is 'WrapEmpty' if 'Nothing' > maybeUnwrap :: Maybe WrapperType -- ^ The potential wrapped type > -> WrapperType -- ^ Definitely a wrapped type (though perhaps an empty one) > maybeUnwrap (Just t) = t > maybeUnwrap Nothing = WrapEmpty $ BStrT 0 > -- | Special version of 'Data.Map.fromList' that also wraps fields as it goes > fromList' :: DBInfo -- ^ Database to use for wrapping info > -> [(FieldName, String)] -- ^ List of pairs associating a field name to some 'String' representation of a value > -> Map FieldName WrapperType -- ^ The map associating field names to wrapped types > fromList' db is = fromList $ map (\x -> let fx = fst x in (fx, wrap db fx $ snd x)) is > unwrapField :: Wrappable a => FieldName -> Fields -> Maybe a > unwrapField fn fs = appMaybe unwrap $ lookup fn fs > instance Binary FieldType where > put StringT = putWord8 0 > put IntT = putWord8 1 > put IntegerT = putWord8 2 > put DoubleT = putWord8 3 > put BoolT = putWord8 4 > put CalendarTimeT = putWord8 5 > put (BStrT i) = do > putWord8 6 > put i > get = do > tag <- getWord8 > case tag of > 0 -> return StringT > 1 -> return IntT > 2 -> return IntegerT > 3 -> return DoubleT > 4 -> return BoolT > 5 -> return CalendarTimeT > 6 -> do > i <- get > return $ BStrT i > instance Binary WrapperType where > put (WrapString mi s) = do > putWord8 0 > put mi > put s > put (WrapInt i) = do > putWord8 1 > put i > put (WrapBool b) = do > putWord8 2 > put b > put (WrapError t s) = do > putWord8 3 > put t > put s > put (WrapEmpty t) = do > putWord8 4 > put t > get = do > tag <- getWord8 > case tag of > 0 -> do > mi <- get > s <- get > return $ WrapString mi s > 1 -> do > i <- get > return $ WrapInt i > 2 -> do > b <- get > return $ WrapBool b > 3 -> do > t <- get > s <- get > return $ WrapError t s > 4 -> do > t <- get > return $ WrapEmpty t