{-|
Module      : Data.RDF.ToRDF
Description : DSL for Mapping Haskell Data to RDF Graphs
Copyright   : Travis Whitaker 2016
License     : MIT
Maintainer  : pi.boy.travis@gmail.com
Stability   : Provisional
Portability : Portable

This module provides a simple DSL for mapping Haskell data to RDF graphs.
-}

{-# LANGUAGE FlexibleContexts
           , FlexibleInstances
           , TupleSections
           #-}

module Data.RDF.ToRDF (
    ToRDF(..)
  , ToObject(..)
  , toTriples
  , Triples
  , RDFGen
  , runRDFGen
  , appBaseIRI
  , newBlankNode
  ) where

import Control.Monad.Trans.Reader
import Control.Monad.Trans.State.Lazy

import qualified Data.DList as DL

import Data.Int

import Data.Monoid

import Data.RDF.Types

import qualified Data.Text                        as T
import qualified Data.Text.Lazy                   as TL
import qualified Data.Text.Lazy.Builder           as TL
import qualified Data.Text.Lazy.Builder.Int       as TL
import qualified Data.Text.Lazy.Builder.RealFloat as TL

import Data.Word

type Triples = DL.DList Triple

-- | RDF generator monad. Provides 'ReaderT' for the base 'IRI', and 'StateT'
--   for a monotonically increasing blank node identifier.
type RDFGen a = ReaderT IRI (State Word64) a

runRDFGen :: RDFGen a -> IRI -> a
runRDFGen :: forall a. RDFGen a -> IRI -> a
runRDFGen RDFGen a
m IRI
i = forall s a. State s a -> s -> a
evalState (forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT RDFGen a
m IRI
i) Word64
0

class ToRDF a where
    triples :: a -> RDFGen Triples

class ToObject a where
    object :: a -> RDFGen Object

instance ToObject Int where
    object :: Int -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Integer where
    object :: Integer -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Int8 where
    object :: Int8 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Int16 where
    object :: Int16 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Int32 where
    object :: Int32 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Int64 where
    object :: Int64 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Word where
    object :: Word -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Word8 where
    object :: Word8 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Word16 where
    object :: Word16 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Word32 where
    object :: Word32 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject Word64 where
    object :: Word64 -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal

instance ToObject String where
    object :: String -> RDFGen Object
object String
s = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Literal -> Object
LiteralObject (Text -> LiteralType -> Literal
Literal (String -> Text
T.pack String
s) LiteralType
LiteralUntyped)

instance ToObject T.Text where
    object :: Text -> RDFGen Object
object Text
t = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Literal -> Object
LiteralObject (Text -> LiteralType -> Literal
Literal Text
t LiteralType
LiteralUntyped)

-- | Forces the lazy 'TL.Text'.
instance ToObject TL.Text where
    object :: Text -> RDFGen Object
object Text
t = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Literal -> Object
LiteralObject (Text -> LiteralType -> Literal
Literal (Text -> Text
TL.toStrict Text
t) LiteralType
LiteralUntyped)

instance ToObject Float where
    object :: Float -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. RealFloat a => a -> Builder
TL.realFloat

instance ToObject Double where
    object :: Double -> RDFGen Object
object = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Object
toLObject forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. RealFloat a => a -> Builder
TL.realFloat

toTriples :: ToRDF a => IRI -> a -> [Triple]
toTriples :: forall a. ToRDF a => IRI -> a -> [Triple]
toTriples IRI
i a
x = forall a. DList a -> [a]
DL.toList (forall a. RDFGen a -> IRI -> a
runRDFGen (forall a. ToRDF a => a -> RDFGen Triples
triples a
x) IRI
i)

toText :: TL.Builder -> T.Text
toText :: Builder -> Text
toText = Text -> Text
TL.toStrict forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
TL.toLazyText

toLObject :: TL.Builder -> Object
toLObject :: Builder -> Object
toLObject Builder
b = Literal -> Object
LiteralObject (Text -> LiteralType -> Literal
Literal (Builder -> Text
toText Builder
b) LiteralType
LiteralUntyped)

appBaseIRI :: Endo IRI -> RDFGen IRI
appBaseIRI :: Endo IRI -> RDFGen IRI
appBaseIRI = forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Endo a -> a -> a
appEndo

newBlankNode :: RDFGen BlankNode
newBlankNode :: RDFGen BlankNode
newBlankNode = forall r (m :: * -> *) a. (r -> m a) -> ReaderT r m a
ReaderT (forall a b. a -> b -> a
const ((Text -> BlankNode
BlankNode forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
toText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Integral a => a -> Builder
TL.decimal)
                           forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) s. Monad m => StateT s m s
get forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
modify' (forall a. Num a => a -> a -> a
+Word64
1)))