packages feed

casa-reoc-0.1.0.0: src/ReOC/Primitive/Text1.hs

{-# LANGUAGE DeriveLift #-}
{-# OPTIONS_GHC -Wall #-}

{- | Text that is not blank (it has at least one character that is not white
space), every character of which XML can hold: no control characters other
than tab, line feed and carriage return, and neither U+FFFE nor U+FFFF. The
documents are XML, so text with any other character would make them invalid.

Like every primitive type, @Text1@ is outside the classy optics recipe: its
constructor is not exported, so that every value keeps its invariant. It is
built with the prism '_Text1' or, for a literal, with 'text1Literal', which is
checked when the program is compiled.
-}
module ReOC.Primitive.Text1 (
  Text1,
  _Text1,
  text1Text,
  text1Literal,
  xmlCharacter,
) where

import Control.Lens (Prism', preview, prism')
import Data.Char (isSpace)
import Data.Text (Text)
import qualified Data.Text as Text
import Language.Haskell.TH.Syntax (Code (..), Lift, Q, liftTyped)

{- $setup
>>> :set -XOverloadedStrings
>>> import Control.Lens ((^?))
-}

-- | Text that is not blank, and that XML can hold.
newtype Text1
  = Text1 Text
  deriving (Eq, Ord, Show, Lift)

{- | Text that is not blank, and that XML can hold.

>>> "Banksia Point" ^? _Text1
Just (Text1 "Banksia Point")

>>> " \t" ^? _Text1
Nothing

>>> "Bad\x01name" ^? _Text1
Nothing
-}
_Text1 :: Prism' Text Text1
_Text1 =
  prism'
    text1Text
    (\t -> if Text.any (not . isSpace) t && Text.all xmlCharacter t then Just (Text1 t) else Nothing)

{- | Whether XML 1.0 can hold a character.

>>> map xmlCharacter "a\t\x01\xFFFE"
[True,True,False,False]
-}
xmlCharacter :: Char -> Bool
xmlCharacter c =
  c == '\t' || c == '\n' || c == '\r' || (c >= '\x20' && c <= '\xD7FF') || (c >= '\xE000' && c <= '\xFFFD') || c >= '\x10000'

-- | The text of a @Text1@.
text1Text :: Text1 -> Text
text1Text (Text1 t) =
  t

{- | A @Text1@ literal, checked when the program is compiled:

> $$(text1Literal "Banksia Point Aerial Media")
-}
text1Literal :: String -> Code Q Text1
text1Literal s =
  maybe
    (Code (fail ("text1Literal: blank, or a character that XML cannot hold: " <> show s)))
    liftTyped
    (preview _Text1 (Text.pack s))