module Data.SirenJSON where
import Data.Aeson ((.=), (.:?), (.!=), (.:), FromJSON (parseJSON), Object, object, ToJSON (toJSON), Value (Object, String), withObject, withText)
import Data.Foldable (asum)
import Data.Functor ((<$>))
import Data.Maybe (catMaybes)
import Data.Monoid ((<>))
import Data.Text (Text)
import Network.HTTP.Media (MediaType)
import Network.HTTP.Types (StdMethod (GET))
import Network.URI.JSON ()
import Network.URI (URI)
import qualified Data.HashMap.Lazy as HashMap (fromList)
import qualified Data.Map.Strict as Map (empty, Map, null)
import External.Network.HTTP.Media.MediaType.JSON ()
import External.Network.HTTP.Types.Method.JSON ()
data Entity = Entity
{ eClass :: [Text]
, eProperties :: Map.Map Text Text
, eEntities :: [SubEntity]
, eLinks :: [Link]
, eActions :: [Action]
, eTitle :: Maybe Text
} deriving (Eq, Show)
instance FromJSON Entity where
parseJSON = withObject "Entity" $ \ v -> do
eClass <- v .:? "class" .!= []
eProperties <- v .:? "properties" .!= Map.empty
eEntities <- v .:? "entities" .!= []
eLinks <- v .:? "links" .!= []
eActions <- v .:? "actions" .!= []
eTitle <- v .:? "title"
return Entity{..}
instance ToJSON Entity where
toJSON Entity{..} = object $ catMaybes
[ if null eClass then Nothing else Just $ "class" .= eClass
, if Map.null eProperties then Nothing else Just $ "properties" .= eProperties
, if null eEntities then Nothing else Just $ "entities" .= eEntities
, if null eLinks then Nothing else Just $ "links" .= eLinks
, if null eActions then Nothing else Just $ "actions" .= eActions
, (.=) "title" <$> eTitle
]
data SubEntity = EmbeddedLink Link
| EmbeddedRepresentation
{ sEntity :: Entity
, sRel :: [Text]
}
deriving (Eq, Show)
instance FromJSON SubEntity where
parseJSON = withObject "SubEntity" $ \ v -> asum
[ EmbeddedLink <$> parseJSON (Object v)
, do sEntity <- parseJSON (Object v)
sRel <- v .: "rel"
return EmbeddedRepresentation{..}
]
instance ToJSON SubEntity where
toJSON (EmbeddedLink l) = toJSON l
toJSON EmbeddedRepresentation{..} = Object $ toObject sEntity <> HashMap.fromList ["rel" .= sRel]
where toObject :: ToJSON a => a -> Object
toObject v = case toJSON v of
Object o -> o
_ -> error "toObject: received non-Object"
data Link = Link
{ lClass :: [Text]
, lRel :: [Text]
, lHref :: URI
, lType :: Maybe MediaType
, lTitle :: Maybe Text
} deriving (Eq, Show)
instance FromJSON Link where
parseJSON = withObject "Link" $ \ v -> do
lClass <- v .:? "class" .!= []
lRel <- v .: "rel"
lHref <- v .: "href"
lType <- v .:? "type"
lTitle <- v .:? "title"
return Link{..}
instance ToJSON Link where
toJSON Link{..} = object $ catMaybes
[ if null lClass then Nothing else Just $ "class" .= lClass
, Just $ "rel" .= lRel
, Just $ "href" .= lHref
, (.=) "type" <$> lType
, (.=) "title" <$> lTitle
]
data Action = Action
{ aName :: Text
, aClass :: [Text]
, aMethod :: Maybe StdMethod
, aHref :: URI
, aTitle :: Maybe Text
, aType :: Maybe MediaType
, aFields :: [Field]
} deriving (Eq, Show)
instance FromJSON Action where
parseJSON = withObject "Action" $ \ v -> do
aName <- v .: "name"
aClass <- v .:? "class" .!= []
aMethod <- v .:? "method" .!= Just GET
aHref <- v .: "href"
aTitle <- v .:? "title"
aType <- v .:? "type"
aFields <- v .:? "fields" .!= []
return Action{..}
instance ToJSON Action where
toJSON Action{..} = object $ catMaybes
[ Just $ "name" .= aName
, if null aClass then Nothing else Just $ "class" .= aClass
, (.=) "method" <$> aMethod
, Just $ "href" .= aHref
, (.=) "title" <$> aTitle
, (.=) "type" <$> (show <$> aType)
, if null aFields then Nothing else Just $ "fields" .= aFields
]
data Field = Field
{ fName :: Text
, fClass :: [Text]
, fType :: Maybe InputType
, fValue :: Maybe Text
, fTitle :: Maybe Text
} deriving (Eq, Show)
instance FromJSON Field where
parseJSON = withObject "Field" $ \ v -> do
fName <- v .: "name"
fClass <- v .:? "class" .!= []
fType <- v .:? "type" .!= Just Text
fValue <- v .:? "value"
fTitle <- v .:? "title"
return Field{..}
instance ToJSON Field where
toJSON Field{..} = object $ catMaybes
[ Just $ "name" .= fName
, if null fClass then Nothing else Just $ "class" .= fClass
, (.=) "type" <$> fType
, (.=) "value" <$> fValue
, (.=) "title" <$> fTitle
]
data InputType = Hidden
| Text
| Search
| Tel
| URL
| Email
| Password
| DateTime
| Date
| Month
| Week
| Time
| DateTimeLocal
| Number
| Range
| Color
| CheckBox
| Radio
| File
deriving (Eq, Show)
instance FromJSON InputType where
parseJSON = withText "InputType" $ \ v ->
case v of
"hidden" -> return Hidden
"text" -> return Text
"search" -> return Search
"tel" -> return Tel
"url" -> return URL
"email" -> return Email
"password" -> return Password
"datetime" -> return DateTime
"date" -> return Date
"month" -> return Month
"week" -> return Week
"time" -> return Time
"datetime-local" -> return DateTimeLocal
"number" -> return Number
"range" -> return Range
"color" -> return Color
"checkbox" -> return CheckBox
"radio" -> return Radio
"file" -> return File
_ -> fail "invalid InputType"
instance ToJSON InputType where
toJSON Hidden = String "hidden"
toJSON Text = String "text"
toJSON Search = String "search"
toJSON Tel = String "tel"
toJSON URL = String "url"
toJSON Email = String "email"
toJSON Password = String "password"
toJSON DateTime = String "datetime"
toJSON Date = String "date"
toJSON Month = String "month"
toJSON Week = String "week"
toJSON Time = String "time"
toJSON DateTimeLocal = String "datetime-local"
toJSON Number = String "number"
toJSON Range = String "range"
toJSON Color = String "color"
toJSON CheckBox = String "checkbox"
toJSON Radio = String "radio"
toJSON File = String "file"
class FromEntity a where
fromEntity :: Entity -> a
class ToEntity a where
toEntity :: a -> Entity