{-|
Module      : Net.DNSBase.RData.TXT
Description : Text-payload RR types (TXT, HINFO, NULL)
Copyright   : (c) Viktor Dukhovni, 2026
License     : BSD-3-Clause
Maintainer  : ietf-dane@dukhovni.org
Stability   : unstable

Three RFC 1035 RR types carrying byte-string payloads.
'T_txt' is the general-purpose record holding one or more
character-strings — used by SPF, DKIM, DMARC, and many ad-hoc
TXT conventions.  'T_hinfo' was defined to describe a host's
hardware and operating system but is rarely used in modern zone
data.  'T_null' is opaque arbitrary bytes; primarily a
historical placeholder, presented using the generic RFC 3597
syntax.
-}
{-# LANGUAGE RecordWildCards #-}

module Net.DNSBase.RData.TXT
    ( T_txt(..)
    , T_hinfo(..)
    , T_null(..)
    ) where

import qualified Data.ByteString.Short as SB

import Net.DNSBase.Internal.Util

import Net.DNSBase.Bytes
import Net.DNSBase.Decode.State
import Net.DNSBase.Encode.State
import Net.DNSBase.Present
import Net.DNSBase.RData
import Net.DNSBase.RRTYPE
import Net.DNSBase.Text

-- | The @TXT@ resource record
-- ([RFC 1035 section 3.3.14](https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14))
-- — a non-empty list of byte-strings, each at most 255 bytes long.
-- Most TXT-record conventions (SPF, DKIM, DMARC, ...) concatenate
-- the strings on read, but the wire format preserves the boundaries.
--
-- The constructor does not enforce the per-string 255-byte limit;
-- encoding fails if any individual string exceeds it.  Values
-- decoded from wire form are always within the limit by
-- construction.
--
-- The 'Ord' instance compares the strings as DNS
-- character-strings (length-prefixed lexicographic), agreeing
-- with the canonical wire-form ordering of
-- [RFC 4034 section 6.2](https://datatracker.ietf.org/doc/html/rfc4034#section-6.2).
newtype T_txt = T_TXT (NonEmpty ShortByteString) -- ^ One or more character-strings
    deriving (T_txt -> T_txt -> Bool
(T_txt -> T_txt -> Bool) -> (T_txt -> T_txt -> Bool) -> Eq T_txt
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: T_txt -> T_txt -> Bool
== :: T_txt -> T_txt -> Bool
$c/= :: T_txt -> T_txt -> Bool
/= :: T_txt -> T_txt -> Bool
Eq, Int -> T_txt -> ShowS
[T_txt] -> ShowS
T_txt -> String
(Int -> T_txt -> ShowS)
-> (T_txt -> String) -> ([T_txt] -> ShowS) -> Show T_txt
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> T_txt -> ShowS
showsPrec :: Int -> T_txt -> ShowS
$cshow :: T_txt -> String
show :: T_txt -> String
$cshowList :: [T_txt] -> ShowS
showList :: [T_txt] -> ShowS
Show)

instance Ord T_txt where
    compare :: T_txt -> T_txt -> Ordering
compare = (T_txt -> NonEmpty DnsText) -> T_txt -> T_txt -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing T_txt -> NonEmpty DnsText
asDnsText
      where
        asDnsText :: T_txt -> NonEmpty DnsText
        asDnsText :: T_txt -> NonEmpty DnsText
asDnsText = T_txt -> NonEmpty DnsText
forall a b. Coercible a b => a -> b
coerce

instance Presentable T_txt where
    present :: T_txt -> Builder -> Builder
present (T_TXT (ShortByteString
str :| [ShortByteString]
strs)) =
        ShortByteString -> Builder -> Builder
pfst ShortByteString
str
        (Builder -> Builder) -> (Builder -> Builder) -> Builder -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> [ShortByteString] -> Builder)
-> [ShortByteString] -> Builder -> Builder
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((ShortByteString -> Builder -> Builder)
-> Builder -> [ShortByteString] -> Builder
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ShortByteString -> Builder -> Builder
pnxt) [ShortByteString]
strs
      where
        pfst :: ShortByteString -> Builder -> Builder
pfst = forall a. Presentable a => a -> Builder -> Builder
present @DnsText (DnsText -> Builder -> Builder)
-> (ShortByteString -> DnsText)
-> ShortByteString
-> Builder
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShortByteString -> DnsText
forall a b. Coercible a b => a -> b
coerce
        pnxt :: ShortByteString -> Builder -> Builder
pnxt = forall a. Presentable a => a -> Builder -> Builder
presentSp @DnsText (DnsText -> Builder -> Builder)
-> (ShortByteString -> DnsText)
-> ShortByteString
-> Builder
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShortByteString -> DnsText
forall a b. Coercible a b => a -> b
coerce

instance KnownRData T_txt where
    rdType :: forall b -> (b ~ T_txt) => RRTYPE
rdType _ = RRTYPE
TXT
    {-# INLINE rdType #-}
    rdEncode :: forall s. T_txt -> SPut s RData
rdEncode (T_TXT NonEmpty ShortByteString
strs) =
        (ShortByteString -> SPutM s RData ())
-> NonEmpty ShortByteString -> SPutM s RData ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ShortByteString -> SPutM s RData ()
forall s. ShortByteString -> SPut s RData
encodeCharString NonEmpty ShortByteString
strs
    rdDecode :: forall b ->
(b ~ T_txt) => RDataExtensionVal T_txt -> Int -> SGet RData
rdDecode _ RDataExtensionVal T_txt
_ Int
len = do
        pos0 <- SGet Int
getPosition
        str  <- getShortByteStringLen8
        used <- subtract pos0 <$> getPosition
        rest <- getVarWidthSequence getShortByteStringLen8 (len - used)
        pure $ RData $ T_TXT $ str :| rest

-- | The @HINFO@ resource record
-- ([RFC 1035 section 3.3.2](https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.2))
-- — host information: a /CPU/ character-string and an /OS/
-- character-string describing the named host's hardware and
-- operating system.  Rarely used in modern zone data; RFC 8482
-- reuses the type code as a placeholder answer for @ANY@ queries.
--
-- > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-- > /                      CPU                      /
-- > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-- > /                       OS                      /
-- > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
--
-- The 'Ord' instance compares both fields as DNS
-- character-strings, giving canonical ordering
-- ([RFC 4034 section 6.2](https://datatracker.ietf.org/doc/html/rfc4034#section-6.2)).
data T_hinfo = T_HINFO
    { T_hinfo -> ShortByteString
hinfoCPU :: ShortByteString
    , T_hinfo -> ShortByteString
hinfoOS  :: ShortByteString
    } deriving (T_hinfo -> T_hinfo -> Bool
(T_hinfo -> T_hinfo -> Bool)
-> (T_hinfo -> T_hinfo -> Bool) -> Eq T_hinfo
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: T_hinfo -> T_hinfo -> Bool
== :: T_hinfo -> T_hinfo -> Bool
$c/= :: T_hinfo -> T_hinfo -> Bool
/= :: T_hinfo -> T_hinfo -> Bool
Eq, Int -> T_hinfo -> ShowS
[T_hinfo] -> ShowS
T_hinfo -> String
(Int -> T_hinfo -> ShowS)
-> (T_hinfo -> String) -> ([T_hinfo] -> ShowS) -> Show T_hinfo
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> T_hinfo -> ShowS
showsPrec :: Int -> T_hinfo -> ShowS
$cshow :: T_hinfo -> String
show :: T_hinfo -> String
$cshowList :: [T_hinfo] -> ShowS
showList :: [T_hinfo] -> ShowS
Show)

instance Ord T_hinfo where
    T_hinfo
a compare :: T_hinfo -> T_hinfo -> Ordering
`compare` T_hinfo
b = T_hinfo -> ShortByteString
hinfoCPU T_hinfo
a ShortByteString -> ShortByteString -> Ordering
`strCompare` T_hinfo -> ShortByteString
hinfoCPU T_hinfo
b
                 Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> T_hinfo -> ShortByteString
hinfoOS  T_hinfo
a ShortByteString -> ShortByteString -> Ordering
`strCompare` T_hinfo -> ShortByteString
hinfoOS  T_hinfo
b
      where
        strCompare :: ShortByteString -> ShortByteString -> Ordering
strCompare = (ShortByteString -> DnsText)
-> ShortByteString -> ShortByteString -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing ShortByteString -> DnsText
DnsText

instance Presentable T_hinfo where
    present :: T_hinfo -> Builder -> Builder
present T_HINFO{ShortByteString
hinfoCPU :: T_hinfo -> ShortByteString
hinfoOS :: T_hinfo -> ShortByteString
hinfoCPU :: ShortByteString
hinfoOS :: ShortByteString
..} =
        forall a. Presentable a => a -> Builder -> Builder
present     @DnsText (ShortByteString -> DnsText
forall a b. Coercible a b => a -> b
coerce ShortByteString
hinfoCPU)
        (Builder -> Builder) -> (Builder -> Builder) -> Builder -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Presentable a => a -> Builder -> Builder
presentSp @DnsText (ShortByteString -> DnsText
forall a b. Coercible a b => a -> b
coerce ShortByteString
hinfoOS)

instance KnownRData T_hinfo where
    rdType :: forall b -> (b ~ T_hinfo) => RRTYPE
rdType _ = RRTYPE
HINFO
    {-# INLINE rdType #-}
    rdEncode :: forall s. T_hinfo -> SPut s RData
rdEncode T_HINFO{ShortByteString
hinfoCPU :: T_hinfo -> ShortByteString
hinfoOS :: T_hinfo -> ShortByteString
hinfoCPU :: ShortByteString
hinfoOS :: ShortByteString
..} = do
        ShortByteString -> SPut s RData
forall s. ShortByteString -> SPut s RData
encodeCharString ShortByteString
hinfoCPU
        ShortByteString -> SPut s RData
forall s. ShortByteString -> SPut s RData
encodeCharString ShortByteString
hinfoOS
    rdDecode :: forall b ->
(b ~ T_hinfo) => RDataExtensionVal T_hinfo -> Int -> SGet RData
rdDecode _ RDataExtensionVal T_hinfo
_ = SGet RData -> Int -> SGet RData
forall a b. a -> b -> a
const do
        T_hinfo -> RData
forall a. KnownRData a => a -> RData
RData (T_hinfo -> RData) -> SGet T_hinfo -> SGet RData
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$.> ShortByteString -> ShortByteString -> T_hinfo
T_HINFO (ShortByteString -> ShortByteString -> T_hinfo)
-> SGet ShortByteString -> SGet (ShortByteString -> T_hinfo)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SGet ShortByteString
getShortByteStringLen8 SGet (ShortByteString -> T_hinfo)
-> SGet ShortByteString -> SGet T_hinfo
forall a b. SGet (a -> b) -> SGet a -> SGet b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SGet ShortByteString
getShortByteStringLen8

-- | The @NULL@ resource record
-- ([RFC 1035 section 3.3.10](https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.10))
-- — arbitrary opaque bytes (up to 65535).  Rarely seen on the
-- wire; presented using the generic
-- [RFC 3597 section 5](https://datatracker.ietf.org/doc/html/rfc3597#section-5)
-- syntax (@\\\# /n/ /hex/@).
--
-- > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-- > /                  <anything>                   /
-- > /                                               /
-- > +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
--
-- Derived 'Ord' is canonical
-- ([RFC 4034 section 6.2](https://datatracker.ietf.org/doc/html/rfc4034#section-6.2)).
newtype T_null = T_NULL Bytes16
    deriving (T_null -> T_null -> Bool
(T_null -> T_null -> Bool)
-> (T_null -> T_null -> Bool) -> Eq T_null
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: T_null -> T_null -> Bool
== :: T_null -> T_null -> Bool
$c/= :: T_null -> T_null -> Bool
/= :: T_null -> T_null -> Bool
Eq, Eq T_null
Eq T_null =>
(T_null -> T_null -> Ordering)
-> (T_null -> T_null -> Bool)
-> (T_null -> T_null -> Bool)
-> (T_null -> T_null -> Bool)
-> (T_null -> T_null -> Bool)
-> (T_null -> T_null -> T_null)
-> (T_null -> T_null -> T_null)
-> Ord T_null
T_null -> T_null -> Bool
T_null -> T_null -> Ordering
T_null -> T_null -> T_null
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: T_null -> T_null -> Ordering
compare :: T_null -> T_null -> Ordering
$c< :: T_null -> T_null -> Bool
< :: T_null -> T_null -> Bool
$c<= :: T_null -> T_null -> Bool
<= :: T_null -> T_null -> Bool
$c> :: T_null -> T_null -> Bool
> :: T_null -> T_null -> Bool
$c>= :: T_null -> T_null -> Bool
>= :: T_null -> T_null -> Bool
$cmax :: T_null -> T_null -> T_null
max :: T_null -> T_null -> T_null
$cmin :: T_null -> T_null -> T_null
min :: T_null -> T_null -> T_null
Ord, Int -> T_null -> ShowS
[T_null] -> ShowS
T_null -> String
(Int -> T_null -> ShowS)
-> (T_null -> String) -> ([T_null] -> ShowS) -> Show T_null
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> T_null -> ShowS
showsPrec :: Int -> T_null -> ShowS
$cshow :: T_null -> String
show :: T_null -> String
$cshowList :: [T_null] -> ShowS
showList :: [T_null] -> ShowS
Show)

instance Presentable T_null where
    present :: T_null -> Builder -> Builder
present (T_NULL Bytes16
val) =
        forall a. Presentable a => a -> Builder -> Builder
present @String String
"\\# "
        (Builder -> Builder) -> (Builder -> Builder) -> Builder -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Builder -> Builder
forall a. Presentable a => a -> Builder -> Builder
present (ShortByteString -> Int
SB.length (ShortByteString -> Int) -> ShortByteString -> Int
forall a b. (a -> b) -> a -> b
$ Bytes16 -> ShortByteString
forall a b. Coercible a b => a -> b
coerce Bytes16
val)
        (Builder -> Builder) -> (Builder -> Builder) -> Builder -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bytes16 -> Builder -> Builder
forall a. Presentable a => a -> Builder -> Builder
presentSp Bytes16
val

instance KnownRData T_null where
    rdType :: forall b -> (b ~ T_null) => RRTYPE
rdType _ = RRTYPE
NULL
    {-# INLINE rdType #-}
    rdEncode :: forall s. T_null -> SPut s RData
rdEncode = ShortByteString -> SPut s RData
forall r s. ErrorContext r => ShortByteString -> SPut s r
putShortByteString (ShortByteString -> SPut s RData)
-> (T_null -> ShortByteString) -> T_null -> SPut s RData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. T_null -> ShortByteString
forall a b. Coercible a b => a -> b
coerce
    rdDecode :: forall b ->
(b ~ T_null) => RDataExtensionVal T_null -> Int -> SGet RData
rdDecode _ RDataExtensionVal T_null
_ = T_null -> RData
forall a. KnownRData a => a -> RData
RData (T_null -> RData)
-> (ShortByteString -> T_null) -> ShortByteString -> RData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bytes16 -> T_null
T_NULL (Bytes16 -> T_null)
-> (ShortByteString -> Bytes16) -> ShortByteString -> T_null
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShortByteString -> Bytes16
forall a b. Coercible a b => a -> b
coerce (ShortByteString -> RData)
-> (Int -> SGet ShortByteString) -> Int -> SGet RData
forall (m :: * -> *) b c a.
Functor m =>
(b -> c) -> (a -> m b) -> a -> m c
<.> Int -> SGet ShortByteString
getShortNByteString

--------------

-- | Encode a DNS /character-string/ (explicit one byte length).
encodeCharString :: ShortByteString -> SPut s RData
encodeCharString :: forall s. ShortByteString -> SPut s RData
encodeCharString = ShortByteString -> SPut s RData
forall r s. ErrorContext r => ShortByteString -> SPut s r
putShortByteStringLen8