packages feed

cuddle-1.7.0.0: test/Test/Codec/CBOR/Cuddle/CDDL/Examples/Huddle.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Test.Codec.CBOR.Cuddle.CDDL.Examples.Huddle (
  huddleRangeArray,
  huddleArray,
  huddleMap,
  huddleRangeMap,
  customGenRule,
  customGenExample,
  refTermExample,
  bytesExample,
  opCertExample,
  sizeTextExample,
  sizeBytesExample,
  rangeListExample,
  rangeMapExample,
  optionalMapExample,
  choicesExample,
  cborControlExample,
  listTooShortExample,
  listSkippedRuleExample,
  listSkippedRuleNestedExample,
  mapLeftoverKVExample,
  mapNoMatchingKeyExample,
  listZeroOrMoreExample,
  mapNestedValueExample,
  deeplyNestedRefExample,
  taggedUintExample,
  tagRangeExample,
) where

import Codec.CBOR.Cuddle.CBOR.Gen (generateFromGRef)
import Codec.CBOR.Cuddle.CBOR.Validator (validateFromGRef)
import Codec.CBOR.Cuddle.CDDL (Name)
import Codec.CBOR.Cuddle.CDDL.Custom.Core (RuleTerm (..))
import Codec.CBOR.Cuddle.Huddle (
  CanQuantify (..),
  Huddle,
  HuddleItem (..),
  Rule,
  Value (..),
  a,
  arr,
  asKey,
  binding,
  collectFrom,
  idx,
  mp,
  opt,
  tag,
  withCBORGen,
  withValidator,
  (...),
  (=:=),
  (==>),
 )
import Codec.CBOR.Cuddle.Huddle qualified as H
import Codec.CBOR.Term qualified as C
import Data.Word (Word64)
import Test.QuickCheck.GenT (MonadGen (..), frequency)

huddleRangeArray :: Huddle
huddleRangeArray =
  collectFrom
    [ HIRule $
        "a"
          =:= arr
            [ opt $ a VInt
            , 2 <+ a VInt +> 3
            , a VBool +> 3
            , 3 <+ a VText
            ]
    ]

huddleArray :: Huddle
huddleArray =
  collectFrom
    [ HIRule $
        "a"
          =:= arr
            [ 0 <+ a VBool
            , 1 <+ a VInt
            , opt $ a VText
            , a VUInt
            ]
    ]

huddleMap :: Huddle
huddleMap =
  collectFrom
    [ HIRule $
        "a"
          =:= mp
            [ idx 1 ==> arr [0 <+ a VUInt]
            , 1 <+ asKey VBytes ==> VAny
            , opt $ idx 2 ==> VBool
            , 0 <+ asKey VText ==> VInt
            ]
    ]

huddleRangeMap :: Huddle
huddleRangeMap =
  collectFrom
    [ HIRule $
        "a"
          =:= mp
            [ 5 <+ asKey VInt ==> VBool +> 10
            ]
    ]

simpleRule :: Name -> Rule
simpleRule n = n =:= arr [1, 2, 3]

customGenRule :: Name -> Rule
customGenRule = withCBORGen (SingleTerm . C.TInt <$> choose (4, 6)) . simpleRule

customGenExample :: Huddle
customGenExample =
  collectFrom
    [ HIRule $ customGenRule "root"
    ]

refTermExample :: Huddle
refTermExample =
  collectFrom
    [ HIRule $ "root" =:= arr [0, a $ customGenRule "bar"]
    ]

bytesExample :: Huddle
bytesExample =
  collectFrom
    [ HIRule $ "root" =:= H.bstr "010203ff"
    ]

opCertExample :: Huddle
opCertExample =
  collectFrom
    [ HIRule $
        "root"
          =:= arr
            [ a (VBytes `H.sized` (32 :: Word64))
            , a VUInt
            , a VUInt
            , a (VBytes `H.sized` (64 :: Word64))
            ]
    ]

sizeTextExample :: Huddle
sizeTextExample =
  collectFrom
    [HIRule $ "root" =:= VText `H.sized` (0 :: Word64, 32 :: Word64)]

sizeBytesExample :: Huddle
sizeBytesExample =
  collectFrom
    [HIRule $ "root" =:= VBytes `H.sized` (0 :: Word64, 32 :: Word64)]

rangeListExample :: Huddle
rangeListExample =
  collectFrom
    [ HIRule $
        "root"
          =:= arr
            [ 3 <+ a VInt +> 7
            ]
    ]

rangeMapExample :: Huddle
rangeMapExample =
  collectFrom
    [ HIRule $
        "root"
          =:= mp
            [ 3 <+ asKey VInt ==> VBool +> 7
            ]
    ]

optionalMapExample :: Huddle
optionalMapExample =
  collectFrom
    [ HIRule $
        "root"
          =:= mp
            [ 10 <+ asKey ((1 :: Integer) ... (10 :: Integer)) ==> VBool
            ]
    ]

choicesExample :: Huddle
choicesExample =
  collectFrom
    [ HIRule $
        "root"
          =:= arr [1, a VInt, 3]
          H./ arr [1, a VBool, 6]
          H./ arr [1, a VText]
    ]

cborControlExample :: Huddle
cborControlExample =
  collectFrom
    [ HIRule $
        "root"
          =:= VBytes
          `H.cbor` simpleRule "simpleRule"
    ]

listTooShortExample :: Huddle
listTooShortExample =
  collectFrom
    [ HIRule $
        "root"
          =:= arr
            [ a VInt
            , a VText
            , a VBool
            ]
    ]

listSkippedRuleExample :: Huddle
listSkippedRuleExample =
  let bar = "bar" =:= VText
   in collectFrom
        [ HIRule $
            "root"
              =:= arr
                [ a VInt
                , opt $ a bar
                ]
        , HIRule bar
        ]

listSkippedRuleNestedExample :: Huddle
listSkippedRuleNestedExample =
  let baz = "baz" =:= arr [a VInt, a VText]
      qux = "qux" =:= VText
      quux = "quux" =:= VInt
   in collectFrom
        [ HIRule $
            "root"
              =:= arr
                [ a VInt
                , opt $ a qux
                , opt $ a baz
                , opt $ a quux
                ]
        , HIRule baz
        , HIRule qux
        , HIRule quux
        ]

mapLeftoverKVExample :: Huddle
mapLeftoverKVExample =
  collectFrom
    [ HIRule $
        "root"
          =:= mp
            [ opt $ idx 1 ==> VText
            , opt $ idx 2 ==> VInt
            , opt $ idx 3 ==> VBool
            ]
    ]

mapNoMatchingKeyExample :: Huddle
mapNoMatchingKeyExample =
  collectFrom
    [ HIRule $
        "root"
          =:= mp
            [ opt $ idx 1 ==> VText
            , opt $ idx 2 ==> VInt
            ]
    ]

listZeroOrMoreExample :: Huddle
listZeroOrMoreExample =
  collectFrom
    [ HIRule $
        "root"
          =:= arr
            [ 0 <+ a VInt
            , a VText
            ]
    ]

mapNestedValueExample :: Huddle
mapNestedValueExample =
  let bar = "bar" =:= arr [a VInt, a VText]
   in collectFrom
        [ HIRule $
            "root"
              =:= mp
                [ opt $ idx 1 ==> bar
                ]
        , HIRule bar
        ]

-- | A rule with deeply nested references competing against a rule that makes
-- actual term progress. Used to test that stacked references don't incorrectly
-- beat rules with real progress.
deeplyNestedRefExample :: Huddle
deeplyNestedRefExample =
  collectFrom
    [ HIRule $ "root" =:= arr [a VInt, opt $ a aRef, opt $ a listRule]
    , HIRule aRef
    , HIRule barRef
    , HIRule bazRef
    , HIRule listRule
    ]
  where
    bazRef = "baz" =:= VText
    barRef = "bar" =:= bazRef
    aRef = "a_ref" =:= barRef
    listRule = "list_rule" =:= arr [a VInt, a VInt]

taggedUintExample :: Huddle
taggedUintExample =
  collectFrom
    [ HIRule $ "root" =:= tag 42 VBytes
    ]

-- | @foo<a> = #6.1280(a) / #6.1400(a)@ with a custom generator that picks
-- any tag in @1280..1400@ (broader than what the schema literally allows)
-- and a custom validator that accepts the same range. Exercised at two
-- different generic instantiations to make sure the bound type is
-- resolved correctly per call.
tagRangeExample :: Huddle
tagRangeExample =
  collectFrom
    [ HIRule $
        "root" =:= arr [a (fooTagRange VUInt), a (fooTagRange VNInt)]
    ]
  where
    fooTagRange :: H.IsType0 t0 => t0 -> H.GRuleCall
    fooTagRange = binding $ \x ->
      withCBORGen (generator x)
        . withValidator (validator x)
        $ "foo" =:= tag 1280 x H./ tag 1400 x
      where
        generator x = do
          tagN <-
            frequency
              [ (1, pure 1280)
              , (1, pure 1400)
              , (4, choose (1281, 1399))
              ]
          generateFromGRef x >>= \case
            SingleTerm inner -> pure $ SingleTerm (C.TTagged tagN inner)
            _ -> error "tagRangeExample: generic argument must be a single term"

        validator x = \case
          SingleTerm (C.TTagged t inner)
            | t >= 1280 && t <= 1400 -> validateFromGRef x inner
            | otherwise -> fail $ "Tag out of range 1280..1400: " <> show t
          wt -> fail $ "Expected a tagged term, got: " <> show wt