{-# language OverloadedStrings #-}

module Prometheus.Export.Text (
    exportMetricsAsText
) where

import Prometheus.Info
import Prometheus.Metric
import Prometheus.Registry

import Control.Monad.IO.Class
import qualified Data.ByteString.Builder as Build
import qualified Data.ByteString.Lazy as BS
import Data.Foldable (foldMap)
import Data.Monoid ((<>), mempty, mconcat)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T


-- $setup
-- >>> :module +Prometheus
-- >>> :set -XOverloadedStrings
-- >>> unregisterAll

-- | Export all registered metrics in the Prometheus 0.0.4 text exposition
-- format.
--
-- For the full specification of the format, see the official Prometheus
-- <http://prometheus.io/docs/instrumenting/exposition_formats/ documentation>.
--
-- >>> :m +Data.ByteString
-- >>> myCounter <- register $ counter (Info "my_counter" "Example counter")
-- >>> incCounter myCounter
-- >>> exportMetricsAsText >>= Data.ByteString.Lazy.putStr
-- # HELP my_counter Example counter
-- # TYPE my_counter counter
-- my_counter 1.0
exportMetricsAsText :: MonadIO m => m BS.ByteString
exportMetricsAsText = do
    samples <- collectMetrics
    return $ Build.toLazyByteString $ foldMap exportSampleGroup samples

exportSampleGroup :: SampleGroup -> Build.Builder
exportSampleGroup (SampleGroup info ty samples) =
    if null samples
        then mempty
        else prefix <> exportedSamples
    where
        exportedSamples = exportSamples samples
        name = metricName info
        help = metricHelp info
        prefix = Build.byteString $ T.encodeUtf8 $ T.unlines [
                "# HELP " <> name <> " " <> T.concatMap escape help
            ,   "# TYPE " <> name <> " " <> T.pack (show ty)
            ]
        escape '\n' = "\\n"
        escape '\\' = "\\\\"
        escape other = T.pack [other]

exportSamples :: [Sample] -> Build.Builder
exportSamples samples =
  mconcat [ exportSample s <> Build.charUtf8 '\n' | s <- samples ]

exportSample :: Sample -> Build.Builder
exportSample (Sample name labels value) =
  Build.byteString (T.encodeUtf8 name)
    <> (case labels of
         [] -> mempty
         l:ls ->
           Build.charUtf8 '{'
             <> exportLabel l
             <> mconcat [ Build.charUtf8 ',' <> exportLabel l' | l' <- ls ]
             <> Build.charUtf8 '}')
    <> Build.charUtf8 ' '
    <> Build.byteString value

exportLabel :: (Text, Text) -> Build.Builder
exportLabel (key, value) =
  Build.byteString (T.encodeUtf8 key)
    <> Build.charUtf8 '='
    <> Build.stringUtf8 (show value)