string-variants-0.1.0.0: src/Data/StringVariants/Prose/Internal.hs
{-# LANGUAGE TemplateHaskell #-}
-- | Internal module of Prose, allowing breaking the abstraction.
--
-- Prefer to use "Data.StringVariants.Prose" instead.
module Data.StringVariants.Prose.Internal where
import Data.Aeson (FromJSON, ToJSON, ToJSONKey, withText)
import Data.Aeson.Types (FromJSON (..))
import Data.String.Conversions (ConvertibleStrings (..), cs)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Text.Lazy qualified as LT
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax
import Prelude
-- | Whitespace-trimmed, non-empty text, for use with API endpoints.
-- The rationale is that there are many situations where if a client sends
-- text that is empty or all whitespace, there's probably a client error.
-- Not suitable for database fields, as there is no character limit (see
-- 'ProsePersistFieldMsg').
newtype Prose = Prose Text
deriving stock (Eq, Lift, Ord, Show)
deriving newtype (Semigroup, ToJSON, ToJSONKey)
instance ConvertibleStrings Prose Text where
convertString (Prose t) = t
instance ConvertibleStrings Prose LT.Text where
convertString (Prose t) = cs t
instance FromJSON Prose where
parseJSON = withText "Prose" $ \t -> case mkProse t of
Nothing -> fail $ "Model/CustomTypes/StringVariants.hs: invalid Prose: " ++ T.unpack t
Just t' -> pure t'
mkProse :: Text -> Maybe Prose
mkProse t = case T.strip t of
"" -> Nothing
t' -> Just (Prose t')
compileProse :: QuasiQuoter
compileProse =
QuasiQuoter
{ quoteDec = error "Prose is not supported at top-level"
, quoteType = error "Prose is not supported as a type"
, quotePat = error "Prose is not a pattern; use `proseToText` instead"
, ..
}
where
quoteExp s = case mkProse (T.pack s) of
Nothing -> fail (msg s)
Just s' -> [|$(lift s')|]
msg s = "Invalid Prose: " <> s <> ". Make sure you aren't wrapping the text in quotes."
proseToText :: Prose -> Text
proseToText (Prose txt) = txt