packages feed

scrod-0.2026.2.21: source/library/Scrod/Xml/Instruction.hs

{-# LANGUAGE TemplateHaskellQuotes #-}

module Scrod.Xml.Instruction where

import qualified Data.ByteString.Builder as Builder
import qualified Data.Text as Text
import qualified Scrod.Extra.Builder as Builder
import qualified Scrod.Extra.Semigroup as Semigroup
import qualified Scrod.Spec as Spec
import qualified Scrod.Xml.Name as Name

-- | XML Processing Instruction, like @\<?name value?>@. If value is empty, no
-- space after the name. Otherwise there must be a space after the name. Cannot
-- contain @?>@.
data Instruction = MkInstruction
  { name :: Name.Name,
    value :: Text.Text
  }
  deriving (Eq, Ord, Show)

encode :: Instruction -> Builder.Builder
encode instr =
  Semigroup.around (Builder.stringUtf8 "<?") (Builder.stringUtf8 "?>") $
    Name.encode (name instr)
      <> if Text.null (value instr)
        then mempty
        else Builder.charUtf8 ' ' <> Builder.stringUtf8 (Text.unpack $ value instr)

spec :: (Applicative m, Monad n) => Spec.Spec m n -> n ()
spec s = do
  Spec.named s 'encode $ do
    Spec.it s "encodes empty value" $ do
      Spec.assertEq s (Builder.toString . encode $ MkInstruction (Name.MkName $ Text.pack "foo") (Text.pack "")) "<?foo?>"

    Spec.it s "encodes with value" $ do
      Spec.assertEq s (Builder.toString . encode $ MkInstruction (Name.MkName $ Text.pack "foo") (Text.pack "bar")) "<?foo bar?>"

    Spec.it s "encodes xml declaration" $ do
      Spec.assertEq s (Builder.toString . encode $ MkInstruction (Name.MkName $ Text.pack "xml") (Text.pack "version=\"1.0\"")) "<?xml version=\"1.0\"?>"