{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
module Servant.Nix where

import Data.Text (Text)
import Network.HTTP.Media ((//))
import Nix
import Servant.API.ContentTypes

import qualified Data.ByteString.Lazy    as LBS
import qualified Data.Text.Lazy          as T
import qualified Data.Text.Lazy.Encoding as T

-- | UTF-8 text representing a Nix expression
data Nix

instance Accept Nix where
  contentType _ = "text" // "nix"

-- | 'NExpr' and 'NExprLoc's can be used as request
--   bodies in servant API types.
instance FromNix a => MimeUnrender Nix a where
  mimeUnrender _ = parseAsNix

instance MimeRender Nix NExpr where
  mimeRender _ = T.encodeUtf8 . T.pack . show . prettyNix

-- | Types that can be parsed out of the text of a Nix
--   expression, i.e 'NExpr' and 'NExprLoc'.
class FromNix a where
  fromNixText :: Text -> Result a

instance FromNix NExpr where
  fromNixText = parseNixText

instance FromNix NExprLoc where
  fromNixText = parseNixTextLoc

parseAsNix :: FromNix a => LBS.ByteString -> Either String a
parseAsNix lbs = case fromNixText (T.toStrict $ T.decodeUtf8 lbs) of
  Failure doc -> Left (show doc)
  Success a   -> pure a