-- |
-- Module      : Net.DNSBase.EDNS.Internal.Option.Opaque
-- Description : Internal: fallback wrapper for unrecognised EDNS options
-- Copyright   : (c) Viktor Dukhovni, 2026
-- License     : BSD-3-Clause
-- Maintainer  : ietf-dane@dukhovni.org
-- Stability   : unstable
module Net.DNSBase.EDNS.Internal.Option.Opaque
    ( OpaqueOption(..)
    , opaqueEdnsOption
    )
    where

import Net.DNSBase.Decode.Internal.State
import Net.DNSBase.EDNS.Internal.OptNum
import Net.DNSBase.EDNS.Internal.Option
import Net.DNSBase.Encode.Internal.State
import Net.DNSBase.Internal.Bytes
import Net.DNSBase.Internal.Nat16
import Net.DNSBase.Internal.Present
import Net.DNSBase.Internal.Util

-- | Unrecognized EDNS Option whose contents are treated as an opaque octet-string
-- and are left unparsed. The OPTION-CODE is encoded as a type-level natural, so
-- opaque options with different option code values are of different types.
type OpaqueOption :: Nat -> Type
data OpaqueOption n = OpaqueOption ShortByteString

deriving instance Eq (OpaqueOption n)

instance Nat16 n => Show (OpaqueOption n) where
    showsPrec :: Int -> OpaqueOption n -> ShowS
showsPrec Int
p (OpaqueOption ShortByteString
bs) = Int -> ShowS -> ShowS
showsP Int
p (ShowS -> ShowS) -> ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
        String -> ShowS
showString String
"OpaqueOption @" ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word16 -> ShowS
forall a. Show a => a -> ShowS
shows (natToWord16 n) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> ShowS
showChar Char
' '
        ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> ShowS
shows @Bytes16 (ShortByteString -> Bytes16
forall a b. Coercible a b => a -> b
coerce ShortByteString
bs)

instance Presentable (OpaqueOption n) where
    present :: OpaqueOption n -> Builder -> Builder
present = \ (OpaqueOption ShortByteString
bs) -> forall a. Presentable a => a -> Builder -> Builder
present @Bytes16 (ShortByteString -> Bytes16
forall a b. Coercible a b => a -> b
coerce ShortByteString
bs)

instance Nat16 n => KnownEdnsOption (OpaqueOption n) where
    optNum :: forall b -> (b ~ OpaqueOption n) => OptNum
optNum _ = Word16 -> OptNum
OptNum (Word16 -> OptNum) -> Word16 -> OptNum
forall a b. (a -> b) -> a -> b
$ natToWord16 n
    {-# INLINE optNum #-}
    optPres :: forall b -> (b ~ OpaqueOption n) => Builder -> Builder
optPres _ = String -> Builder -> Builder
forall a. Presentable a => a -> Builder -> Builder
present String
"OPT" (Builder -> Builder) -> (Builder -> Builder) -> Builder -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word16 -> Builder -> Builder
forall a. Presentable a => a -> Builder -> Builder
present (natToWord16 n)
    optEncode :: forall s r.
(Typeable r, Eq r, Show r) =>
OpaqueOption n -> SPut s r
optEncode (OpaqueOption ShortByteString
bs) = ShortByteString -> SPut s r
forall r s. ErrorContext r => ShortByteString -> SPut s r
putShortByteString (ShortByteString -> SPut s r) -> ShortByteString -> SPut s r
forall a b. (a -> b) -> a -> b
$ ShortByteString -> ShortByteString
forall a b. Coercible a b => a -> b
coerce ShortByteString
bs
    optDecode :: forall b ->
(b ~ OpaqueOption n) =>
OptionExtensionVal b -> Int -> SGet EdnsOption
optDecode _ OptionExtensionVal b
_ = OpaqueOption n -> EdnsOption
forall a. KnownEdnsOption a => a -> EdnsOption
EdnsOption (OpaqueOption n -> EdnsOption)
-> (ShortByteString -> OpaqueOption n)
-> ShortByteString
-> EdnsOption
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (n :: Nat). ShortByteString -> OpaqueOption n
OpaqueOption @n (ShortByteString -> EdnsOption)
-> (Int -> SGet ShortByteString) -> Int -> SGet EdnsOption
forall (m :: * -> *) b c a.
Functor m =>
(b -> c) -> (a -> m b) -> a -> m c
<.> Int -> SGet ShortByteString
getShortNByteString

-- | Create opaque option from its opcode and Bytes16 value
opaqueEdnsOption :: Word16 -> ShortByteString -> EdnsOption
opaqueEdnsOption :: Word16 -> ShortByteString -> EdnsOption
opaqueEdnsOption Word16
w ShortByteString
bs = Word16
-> (forall (n :: Nat) -> Nat16 n => EdnsOption) -> EdnsOption
forall r. Word16 -> (forall (n :: Nat) -> Nat16 n => r) -> r
withNat16 Word16
w forall (n :: Nat) -> Nat16 n => EdnsOption
go
  where
    go :: forall (n :: Nat) -> Nat16 n => EdnsOption
    go :: forall (n :: Nat) -> Nat16 n => EdnsOption
go n = OpaqueOption n -> EdnsOption
forall a. KnownEdnsOption a => a -> EdnsOption
EdnsOption (OpaqueOption n -> EdnsOption) -> OpaqueOption n -> EdnsOption
forall a b. (a -> b) -> a -> b
$ forall (n :: Nat). ShortByteString -> OpaqueOption n
OpaqueOption @n ShortByteString
bs