summaryrefslogtreecommitdiff
path: root/src/Language/GraphQL/TH.hs
blob: 6e2058633823b1c64de90d27ef91102a8c25f0ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{- This Source Code Form is subject to the terms of the Mozilla Public License,
   v. 2.0. If a copy of the MPL was not distributed with this file, You can
   obtain one at https://mozilla.org/MPL/2.0/. -}

module Language.GraphQL.TH
    ( gql
    ) where

import Language.Haskell.TH
    ( Exp(..)
    , Lit(..)
    )
import Language.Haskell.TH.Quote (QuasiQuoter(..))

stripIndentation :: String -> String
stripIndentation code = reverse
    $ dropWhile isLineBreak
    $ reverse
    $ unlines
    $ indent spaces <$> lines' withoutLeadingNewlines
  where
    indent 0 xs = xs
    indent count (' ' : xs) = indent (count - 1) xs
    indent _ xs = xs
    withoutLeadingNewlines = dropWhile isLineBreak code
    spaces = length $ takeWhile (== ' ') withoutLeadingNewlines
    lines' "" = []
    lines' string =
        let (line, rest) = break isLineBreak string
            reminder =
                case rest of
                    [] -> []
                    '\r' : '\n' : strippedString -> lines' strippedString
                    _ : strippedString -> lines' strippedString
         in line : reminder
    isLineBreak = flip any ['\n', '\r'] . (==)

-- | Removes leading and trailing newlines. Indentation of the first line is
-- removed from each line of the string.
gql :: QuasiQuoter
gql = QuasiQuoter
    { quoteExp = pure . LitE . StringL . stripIndentation
    , quotePat = const
        $ fail "Illegal gql QuasiQuote (allowed as expression only, used as a pattern)"
    , quoteType = const
        $ fail "Illegal gql QuasiQuote (allowed as expression only, used as a type)"
    , quoteDec = const
        $ fail "Illegal gql QuasiQuote (allowed as expression only, used as a declaration)"
    }