module Saturn.Unstable.Type.ElementSpec where

import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Word as Word
import qualified Saturn.Unstable.Type.Element as Element
import qualified Saturn.Unstable.Type.Number as Number
import qualified Saturn.Unstable.Type.NumberSpec as NumberSpec
import qualified Saturn.Unstable.Type.Range as Range
import qualified Saturn.Unstable.Type.RangeSpec as RangeSpec
import qualified Test.Hspec as Hspec
import qualified Test.QuickCheck as QuickCheck
import qualified Text.Parsec as Parsec

spec :: Hspec.Spec
spec :: Spec
spec = forall a. HasCallStack => String -> SpecWith a -> SpecWith a
Hspec.describe String
"Saturn.Unstable.Type.Element" forall a b. (a -> b) -> a -> b
$ do
  forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
Hspec.it String
"round trips"
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> [a]) -> (a -> prop) -> Property
QuickCheck.forAllShrink Gen Element
arbitrary Element -> [Element]
shrink
    forall a b. (a -> b) -> a -> b
$ \Element
x -> do
      forall s t a.
Stream s Identity t =>
Parsec s () a -> String -> s -> Either ParseError a
Parsec.parse forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Element
Element.parsec String
"" (Builder -> Text
Builder.toLazyText forall a b. (a -> b) -> a -> b
$ Element -> Builder
Element.toBuilder Element
x)
        forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`Hspec.shouldBe` forall a b. b -> Either a b
Right Element
x

arbitrary :: QuickCheck.Gen Element.Element
arbitrary :: Gen Element
arbitrary =
  Either Range Number -> Element
Element.fromEither
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (f :: * -> * -> *) a b.
Arbitrary2 f =>
Gen a -> Gen b -> Gen (f a b)
QuickCheck.liftArbitrary2
      Gen Range
RangeSpec.arbitrary
      Gen Number
NumberSpec.arbitrary

shrink :: Element.Element -> [Element.Element]
shrink :: Element -> [Element]
shrink Element
element =
  let xs :: [Element]
xs = case Element -> Either Range Number
Element.toEither Element
element of
        Left Range
range ->
          let (Number
lo, Number
hi) = Range -> (Number, Number)
Range.toTuple Range
range
           in forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Either Range Number -> Element
Element.fromEither forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. b -> Either a b
Right) [Number
lo, Number
hi]
        Right Number
_ -> []
   in forall a. Monoid a => a -> a -> a
mappend [Element]
xs
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Either Range Number -> Element
Element.fromEither
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> * -> *) a b.
Arbitrary2 f =>
(a -> [a]) -> (b -> [b]) -> f a b -> [f a b]
QuickCheck.liftShrink2 Range -> [Range]
RangeSpec.shrink Number -> [Number]
NumberSpec.shrink
        forall a b. (a -> b) -> a -> b
$ Element -> Either Range Number
Element.toEither Element
element

new :: (MonadFail m) => [Word.Word8] -> m Element.Element
new :: forall (m :: * -> *). MonadFail m => [Word8] -> m Element
new [Word8]
xs = case [Word8]
xs of
  [Word8
x] -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either Range Number -> Element
Element.fromEither forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Word8 -> Number
Number.fromWord8 Word8
x
  [Word8
x, Word8
y] -> Either Range Number -> Element
Element.fromEither forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *). MonadFail m => (Word8, Word8) -> m Range
RangeSpec.new (Word8
x, Word8
y)
  [Word8]
_ -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"invalid Element: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show [Word8]
xs