{-|
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  = Builder
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
"<" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> IRI -> Builder
encodeIRI IRI
i Builder -> Builder -> Builder
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
                         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
":"
                         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (IRIAuth -> Builder
encodeIRIAuth (IRIAuth -> Builder) -> Maybe IRIAuth -> Maybe Builder
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe IRIAuth
a)
                         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
"/"
                         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
p
                         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((ByteString -> Builder
B.byteString ByteString
"?" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>) (Builder -> Builder) -> (Text -> Builder) -> Text -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) (Text -> Builder) -> Maybe Text -> Maybe Builder
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
q)
                         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((ByteString -> Builder
B.byteString ByteString
"#" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>) (Builder -> Builder) -> (Text -> Builder) -> Text -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) (Text -> Builder) -> Maybe Text -> Maybe Builder
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
"//"
                             Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
"@") (Builder -> Builder) -> (Text -> Builder) -> Text -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) (Text -> Builder) -> Maybe Text -> Maybe Builder
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
u)
                             Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
h
                             Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Maybe Builder -> Builder
maybeBuilder (((ByteString -> Builder
B.byteString ByteString
":" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>) (Builder -> Builder) -> (Text -> Builder) -> Text -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
T.encodeUtf8Builder) (Text -> Builder) -> Maybe Text -> Maybe Builder
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
"\""
                           Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder (Text -> Text
quoteString Text
v)
                           Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
B.byteString ByteString
"\""
                           Builder -> Builder -> Builder
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
"^^"
                                     Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> IRI -> Builder
encodeEscapedIRI IRI
i
encodeLiteralType (LiteralLangType Text
l) = ByteString -> Builder
B.byteString ByteString
"@"
                                     Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
T.encodeUtf8Builder Text
l
encodeLiteralType LiteralType
LiteralUntyped      = Builder
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
"_:" Builder -> Builder -> Builder
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