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 = String -> Spec -> Spec
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
Hspec.describe String
"Saturn.Unstable.Type.Element" (Spec -> Spec) -> Spec -> Spec
forall a b. (a -> b) -> a -> b
$ do
  String -> Property -> SpecWith (Arg Property)
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
Hspec.it String
"round trips"
    (Property -> Spec)
-> ((Element -> Expectation) -> Property)
-> (Element -> Expectation)
-> Spec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Gen Element
-> (Element -> [Element]) -> (Element -> Expectation) -> Property
forall a prop.
(Show a, Testable prop) =>
Gen a -> (a -> [a]) -> (a -> prop) -> Property
QuickCheck.forAllShrink Gen Element
arbitrary Element -> [Element]
shrink
    ((Element -> Expectation) -> Spec)
-> (Element -> Expectation) -> Spec
forall a b. (a -> b) -> a -> b
$ \Element
x -> do
      Parsec Text () Element
-> String -> Text -> Either ParseError Element
forall s t a.
Stream s Identity t =>
Parsec s () a -> String -> s -> Either ParseError a
Parsec.parse Parsec Text () Element
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Element
Element.parsec String
"" (Builder -> Text
Builder.toLazyText (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$ Element -> Builder
Element.toBuilder Element
x)
        Either ParseError Element
-> Either ParseError Element -> Expectation
forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
`Hspec.shouldBe` Element -> Either ParseError Element
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
    (Either Range Number -> Element)
-> Gen (Either Range Number) -> Gen Element
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Gen Range -> Gen Number -> Gen (Either Range Number)
forall a b. Gen a -> Gen b -> Gen (Either a 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 (Number -> Element) -> [Number] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Either Range Number -> Element
Element.fromEither (Either Range Number -> Element)
-> (Number -> Either Range Number) -> Number -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Number -> Either Range Number
forall a b. b -> Either a b
Right) [Number
lo, Number
hi]
        Right Number
_ -> []
   in [Element] -> [Element] -> [Element]
forall a. Monoid a => a -> a -> a
mappend [Element]
xs
        ([Element] -> [Element])
-> (Either Range Number -> [Element])
-> Either Range Number
-> [Element]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Either Range Number -> Element)
-> [Either Range Number] -> [Element]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Either Range Number -> Element
Element.fromEither
        ([Either Range Number] -> [Element])
-> (Either Range Number -> [Either Range Number])
-> Either Range Number
-> [Element]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Range -> [Range])
-> (Number -> [Number])
-> Either Range Number
-> [Either Range Number]
forall a b. (a -> [a]) -> (b -> [b]) -> Either a b -> [Either a b]
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
        (Either Range Number -> [Element])
-> Either Range Number -> [Element]
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] -> Element -> m Element
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Element -> m Element)
-> (Number -> Element) -> Number -> m Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either Range Number -> Element
Element.fromEither (Either Range Number -> Element)
-> (Number -> Either Range Number) -> Number -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Number -> Either Range Number
forall a b. b -> Either a b
Right (Number -> m Element) -> Number -> m Element
forall a b. (a -> b) -> a -> b
$ Word8 -> Number
Number.fromWord8 Word8
x
  [Word8
x, Word8
y] -> Either Range Number -> Element
Element.fromEither (Either Range Number -> Element)
-> (Range -> Either Range Number) -> Range -> Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Range -> Either Range Number
forall a b. a -> Either a b
Left (Range -> Element) -> m Range -> m Element
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Word8, Word8) -> m Range
forall (m :: * -> *). MonadFail m => (Word8, Word8) -> m Range
RangeSpec.new (Word8
x, Word8
y)
  [Word8]
_ -> String -> m Element
forall a. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> m Element) -> String -> m Element
forall a b. (a -> b) -> a -> b
$ String
"invalid Element: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [Word8] -> String
forall a. Show a => a -> String
show [Word8]
xs