{-|
Module      : Data.RDF.Encode.Common
Description : Representation and Incremental Processing of RDF Data
Copyright   : Travis Whitaker 2016
License     : MIT
Maintainer  : pi.boy.travis@gmail.com
Stability   : Provisional
Portability : Portable

This module provides encoders for the primitive terms in the RDF abstract syntax
as described in RDF 1.1 Concepts and Abstract Syntax. These should be useful for
all RDF host languages.
-}

{-# LANGUAGE OverloadedStrings #-}

module Data.RDF.Encoder.Common (
    -- * Triple Components
    encodeSubject
  , encodePredicate
  , encodeObject
    -- * Terms
  , encodeBlankNode
  , encodeLiteral
    -- ** IRIs
  , encodeIRI
  , encodeEscapedIRI
    -- * Utilities
  , quoteString
  , maybeBuilder
  ) where

import qualified Data.ByteString.Builder as B

import Data.RDF.Types

import qualified Data.Text               as T
import qualified Data.Text.Encoding      as T

-- | Escape the double quotes in a quoted string literal.
quoteString :: T.Text -> T.Text
quoteString :: Text -> Text
quoteString = Text -> Text -> Text -> Text
T.replace Text
"\"" Text
"\\\""

-- | Maps 'Nothing' to 'mempty'.
maybeBuilder :: Maybe B.Builder -> B.Builder
maybeBuilder :: Maybe Builder -> Builder
maybeBuilder Maybe Builder
Nothing  = forall a. Monoid a => a
mempty
maybeBuilder (Just Builder
b) = Builder
b

-- | Encode an escaped 'IRI', i.e. between angle brackets.
encodeEscapedIRI :: IRI -> B.Builder
encodeEscapedIRI :: IRI -> Builder
encodeEscapedIRI IRI
i = ByteString -> Builder
B.byteString ByteString
"<" forall a. Semigroup a => a -> a -> a
<> IRI -> Builder
encodeIRI IRI
i forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
">"

-- | Encode an 'IRI'.
encodeIRI :: IRI -> B.Builder
encodeIRI :: IRI -> Builder
encodeIRI (IRI Text
s Maybe IRIAuth
a Text
p Maybe Text
q Maybe Text
f) = Text -> Builder
T.encodeUtf8Builder Text
s
                         forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
":"
                         forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (IRIAuth -> Builder
encodeIRIAuth forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe IRIAuth
a)
                         forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
"/"
                         forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
p
                         forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((ByteString -> Builder
B.byteString ByteString
"?" forall a. Semigroup a => a -> a -> a
<>) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
q)
                         forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((ByteString -> Builder
B.byteString ByteString
"#" forall a. Semigroup a => a -> a -> a
<>) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
f)

-- | Encode an 'IRIAuth'.
encodeIRIAuth :: IRIAuth -> B.Builder
encodeIRIAuth :: IRIAuth -> Builder
encodeIRIAuth (IRIAuth Maybe Text
u Text
h Maybe Text
p) = ByteString -> Builder
B.byteString ByteString
"//"
                             forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
"@") forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
u)
                             forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
h
                             forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((ByteString -> Builder
B.byteString ByteString
":" forall a. Semigroup a => a -> a -> a
<>) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
p)

-- | Encode a 'Literal', including the 'LiteralType'.
encodeLiteral :: Literal -> B.Builder
encodeLiteral :: Literal -> Builder
encodeLiteral (Literal Text
v LiteralType
t) = ByteString -> Builder
B.byteString ByteString
"\""
                           forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder (Text -> Text
quoteString Text
v)
                           forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
"\""
                           forall a. Semigroup a => a -> a -> a
<> LiteralType -> Builder
encodeLiteralType LiteralType
t

-- | Encode a 'LiteralType'.
encodeLiteralType :: LiteralType -> B.Builder
encodeLiteralType :: LiteralType -> Builder
encodeLiteralType (LiteralIRIType IRI
i)  = ByteString -> Builder
B.byteString ByteString
"^^"
                                     forall a. Semigroup a => a -> a -> a
<> IRI -> Builder
encodeEscapedIRI IRI
i
encodeLiteralType (LiteralLangType Text
l) = ByteString -> Builder
B.byteString ByteString
"@"
                                     forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
l
encodeLiteralType LiteralType
LiteralUntyped      = forall a. Monoid a => a
mempty

-- | Encode a 'BlankNode'.
encodeBlankNode :: BlankNode -> B.Builder
encodeBlankNode :: BlankNode -> Builder
encodeBlankNode (BlankNode Text
l) = ByteString -> Builder
B.byteString ByteString
"_:" forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
l

-- | Encode a 'Subject'.
encodeSubject :: Subject -> B.Builder
encodeSubject :: Subject -> Builder
encodeSubject (IRISubject IRI
i)   = IRI -> Builder
encodeEscapedIRI IRI
i
encodeSubject (BlankSubject BlankNode
b) = BlankNode -> Builder
encodeBlankNode BlankNode
b

-- | Encode a 'Predicate'.
encodePredicate :: Predicate -> B.Builder
encodePredicate :: Predicate -> Builder
encodePredicate (Predicate IRI
i) = IRI -> Builder
encodeEscapedIRI IRI
i

-- | Encode a 'Object'.
encodeObject :: Object -> B.Builder
encodeObject :: Object -> Builder
encodeObject (IRIObject IRI
i)     = IRI -> Builder
encodeEscapedIRI IRI
i
encodeObject (LiteralObject Literal
l) = Literal -> Builder
encodeLiteral Literal
l
encodeObject (BlankObject BlankNode
b)   = BlankNode -> Builder
encodeBlankNode BlankNode
b