Skip to content

Commit d117450

Browse files
authored
Merge pull request #14 from AnisimoffNikita/master
Schema generator
2 parents 868dab9 + a8ac1ae commit d117450

File tree

14 files changed

+552
-46
lines changed

14 files changed

+552
-46
lines changed

schematic.cabal

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ cabal-version: >=1.10
1515
library
1616
exposed-modules: Data.Schematic
1717
, Data.Schematic.DSL
18+
, Data.Schematic.Generator
19+
, Data.Schematic.Generator.Regex
1820
, Data.Schematic.Instances
1921
, Data.Schematic.JsonSchema
2022
, Data.Schematic.Helpers
@@ -23,6 +25,11 @@ library
2325
, Data.Schematic.Path
2426
, Data.Schematic.Schema
2527
, Data.Schematic.Validation
28+
, Data.Schematic.Verifier
29+
, Data.Schematic.Verifier.Array
30+
, Data.Schematic.Verifier.Common
31+
, Data.Schematic.Verifier.Number
32+
, Data.Schematic.Verifier.Text
2633
ghc-options: -Wall
2734
default-extensions: ConstraintKinds
2835
, DataKinds

src/Data/Schematic/Generator.hs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
module Data.Schematic.Generator where
2+
3+
import Data.Maybe
4+
import Data.Schematic.Generator.Regex
5+
import {-# SOURCE #-} Data.Schematic.Schema
6+
import Data.Schematic.Verifier
7+
import Data.Scientific
8+
import Data.Text (Text, pack)
9+
import qualified Data.Vector as V
10+
import Test.SmallCheck.Series
11+
12+
maxHigh :: Int
13+
maxHigh = 30
14+
15+
minLow :: Int
16+
minLow = 2
17+
18+
textLengthSeries :: Monad m => [VerifiedTextConstraint] -> Series m Text
19+
textLengthSeries =
20+
\case
21+
[VTEq eq] -> pure $ pack $ take (fromIntegral eq) $ cycle "sample"
22+
[VTBounds ml mh] -> do
23+
let
24+
l = fromMaybe minLow (fromIntegral <$> ml) + 1
25+
h = fromMaybe maxHigh (fromIntegral <$> mh) - 1
26+
n <- generate $ \depth -> take depth [l .. h]
27+
pure $ pack $ take (fromIntegral n) $ cycle "sample"
28+
_ -> pure "error"
29+
30+
textEnumSeries :: Monad m => [Text] -> Series m Text
31+
textEnumSeries enum = generate $ \depth -> take depth enum
32+
33+
textSeries :: Monad m => [DemotedTextConstraint] -> Series m Text
34+
textSeries cs = do
35+
let mvcs = verifyTextConstraints cs
36+
case mvcs of
37+
Just vcs -> do
38+
n <- textSeries' vcs
39+
pure n
40+
Nothing -> pure "error"
41+
42+
textSeries' :: Monad m => [VerifiedTextConstraint] -> Series m Text
43+
textSeries' [] = pure "sample"
44+
textSeries' vcs = do
45+
let enums = listToMaybe [x | VTEnum x <- vcs]
46+
case enums of
47+
Just e -> textEnumSeries e
48+
Nothing -> do
49+
let regexps = listToMaybe [x | VTRegex x _ _ <- vcs]
50+
case regexps of
51+
Just e -> regexSeries e
52+
Nothing -> textLengthSeries vcs
53+
54+
numberSeries :: Monad m => [DemotedNumberConstraint] -> Series m Scientific
55+
numberSeries cs = do
56+
let mvcs = verifyNumberConstraints cs
57+
case mvcs of
58+
Just vcs -> do
59+
n <- numberSeries' vcs
60+
pure $ n
61+
Nothing -> pure 0
62+
63+
numberSeries' :: Monad m => VerifiedNumberConstraint -> Series m Scientific
64+
numberSeries' =
65+
\case
66+
VNEq eq -> pure $ fromIntegral eq
67+
VNBounds ml mh -> do
68+
let l = fromMaybe minLow (fromIntegral <$> ml) + 1
69+
h = fromMaybe maxHigh (fromIntegral <$> mh) - 1
70+
n <- generate $ \depth -> take depth [l .. h]
71+
pure $ fromIntegral n
72+
73+
arraySeries
74+
:: (Monad m, Serial m (JsonRepr s))
75+
=> [DemotedArrayConstraint]
76+
-> Series m (V.Vector (JsonRepr s))
77+
arraySeries cs = do
78+
let mvcs = verifyArrayConstraint cs
79+
case mvcs of
80+
Just vcs -> arraySeries' vcs
81+
Nothing -> pure V.empty
82+
83+
arraySeries'
84+
:: forall m s. (Monad m, Serial m (JsonRepr s))
85+
=> Maybe VerifiedArrayConstraint
86+
-> Series m (V.Vector (JsonRepr s))
87+
arraySeries' ml = do
88+
objs <- V.replicateM (maybe minRepeat f ml) (series :: Series m (JsonRepr s))
89+
pure $ objs
90+
where
91+
f (VAEq l) = fromIntegral l
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module Data.Schematic.Generator.Regex where
2+
3+
import Control.Monad
4+
import Data.List
5+
import Data.Maybe
6+
import qualified Data.Set as S
7+
import Data.Text (Text, unpack)
8+
import Data.Text.Lazy (toStrict)
9+
import Data.Text.Lazy.Builder (Builder, singleton, toLazyText)
10+
import Test.SmallCheck.Series
11+
import Text.Regex.TDFA.Pattern
12+
import Text.Regex.TDFA.ReadRegex (parseRegex)
13+
14+
15+
minRepeat :: Int
16+
minRepeat = 2
17+
18+
maxRepeat :: Int
19+
maxRepeat = 10
20+
21+
regexSeries :: (Monad m) => Text -> Series m Text
22+
regexSeries regexp =
23+
case parseRegex . unpack $ regexp of
24+
Right (p, _) -> toStrict . toLazyText <$> regexSeries' p
25+
Left _ -> pure ""
26+
27+
regexSeries' :: (Monad m) => Pattern -> Series m Builder
28+
regexSeries' pt =
29+
case pt of
30+
PEmpty -> pure mempty
31+
PChar {..} -> pure $ singleton getPatternChar
32+
PAny {getPatternSet = PatternSet (Just cset) _ _ _} -> do
33+
x <- generate $ \depth -> take depth $ S.toList cset
34+
pure $ singleton x
35+
PAnyNot {getPatternSet = PatternSet (Just cset) _ _ _} -> do
36+
x <-
37+
generate $ \depth ->
38+
take depth $ notChars $ concatMap expandEscape $ S.toList cset
39+
pure $ singleton x
40+
PQuest p -> regexSeries' p \/ pure mempty
41+
PPlus p -> regexSeries' $ PBound 1 Nothing p
42+
PStar _ p -> regexSeries' $ PBound 0 Nothing p
43+
PBound low mhigh p -> do
44+
let high = fromMaybe (low + maxRepeat) mhigh
45+
n <- generate $ \depth -> take depth [low .. high]
46+
decDepth $ do
47+
ps <- replicateM n $ regexSeries' p
48+
pure $ mconcat ps
49+
PConcat ps -> mconcat <$> mapM regexSeries' ps
50+
POr xs -> regexSeries' =<< (generate $ \depth -> take depth xs)
51+
PDot _ -> do
52+
x <- generate $ \depth -> take depth $ notChars []
53+
pure $ singleton x
54+
PEscape {..} -> do
55+
x <- generate $ \depth -> take depth $ expandEscape getPatternChar
56+
pure $ singleton x
57+
PCarat _ -> pure mempty
58+
PDollar _ -> pure mempty
59+
_ -> pure mempty
60+
where
61+
notChars = ([' ' .. '~'] \\)
62+
expandEscape ch =
63+
case ch of
64+
'n' -> "\n"
65+
't' -> "\t"
66+
'r' -> "\r"
67+
'f' -> "\f"
68+
'a' -> "\a"
69+
'e' -> "\ESC"
70+
'd' -> ['0' .. '9']
71+
'w' -> ['0' .. '9'] ++ '_' : ['a' .. 'z'] ++ ['A' .. 'Z']
72+
's' -> "\9\32"
73+
'D' -> notChars $ ['0' .. '9']
74+
'W' -> notChars $ ['0' .. '9'] ++ '_' : ['a' .. 'z'] ++ ['A' .. 'Z']
75+
'S' -> notChars "\9\32"
76+
ch' -> [ch']

src/Data/Schematic/Instances.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module Data.Schematic.Instances where
55

66
import Data.Scientific
7+
import Data.Text (Text, pack)
78
import Data.Vector as V
89
import Data.Vinyl
910
import Test.SmallCheck.Series
@@ -21,3 +22,6 @@ instance Serial m a => Serial m (V.Vector a) where
2122

2223
instance Monad m => Serial m Scientific where
2324
series = scientific <$> series <*> series
25+
26+
instance Monad m => Serial m Text where
27+
series = pack <$> series

src/Data/Schematic/Path.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module Data.Schematic.Path where
22

33
import Data.Foldable as F
4-
import Data.Monoid
54
import Data.Singletons.Prelude
65
import Data.Singletons.TypeLits
76
import Data.Text as T

0 commit comments

Comments
 (0)