on develop help sign in
Trimming unused imports and tidying up compilation warnings
commitabee599488e2fabffdfe46309d0153f9d6037fc9 authorBrendan Hay <brendan.g.hay@gmail.com> dateThu 24 Sep 2015, 12:54 (11 years ago) parenta6b3ec1
10 files changed+303−227
gen/gozen-gen.cabal+2−2 gen/src/Gen/AST.hs+46−51 gen/src/Gen/Formatting.hs+1−1 gen/src/Gen/Solve.hs+41−48 gen/src/Gen/Syntax.hs+57−32 gen/src/Gen/Text.hs+18−23 gen/src/Gen/Tree.hs+2−5 gen/src/Gen/Types.hs+51−64 gen/src/Gen/Types/Id.hs+79 gen/src/Main.hs+6−1
gen/gozen-gen.cabal+2−2
@@ -29,6 +29,7 @@ executable gozen-gen
2929 , Gen.Text
3030 , Gen.Tree
3131 , Gen.Types
32+ , Gen.Types.Id
3233 , Gen.Types.Map
3334 , Gen.Types.NS
3435
@@ -38,7 +39,6 @@ executable gozen-gen
3839 aeson
3940 , attoparsec
4041 , base == 4.8.*
41− , bifunctors
4242 , bytestring
4343 , directory-tree
4444 , ede
@@ -48,9 +48,9 @@ executable gozen-gen
4848 , haskell-src-exts
4949 , hindent
5050 , lens
51− , mmorph
5251 , mtl
5352 , optparse-applicative
53+ , parsec
5454 , semigroups
5555 , system-fileio
5656 , system-filepath
gen/src/Gen/AST.hs+46−51
@@ -9,6 +9,7 @@
99{-# LANGUAGE RankNTypes #-}
1010{-# LANGUAGE RecordWildCards #-}
1111{-# LANGUAGE TemplateHaskell #-}
12+{-# LANGUAGE ViewPatterns #-}
1213
1314-- Module : Gen.AST
1415-- Copyright : (c) 2015 Brendan Hay
@@ -21,30 +22,17 @@ module Gen.AST where
2122
2223import Control.Applicative
2324import Control.Error
24−import Control.Lens
25+import Control.Lens hiding (lens)
2526import Control.Monad.Except
26−import Control.Monad.Morph
2727import Control.Monad.State
28−import Data.Aeson hiding (String)
29−import Data.Aeson.TH
30−import qualified Data.Attoparsec.Text as A
3128import Data.Char
3229import Data.Function (on)
3330import qualified Data.HashMap.Strict as Map
34−import Data.List (groupBy, intersect, nub, sort)
35−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
36−import qualified Data.List.NonEmpty as NE
37−import Data.Ord
38−import Data.Semigroup hiding (Sum)
39−import Data.Text (Text)
40−import qualified Data.Text as Text
31+import Data.List (groupBy)
32+import Data.List.NonEmpty (NonEmpty (..))
33+import Data.Semigroup ((<>))
4134import qualified Data.Text.Lazy as LText
4235import qualified Data.Text.Lazy.Builder as Build
43−import Data.Text.Manipulate
44−import Debug.Trace
45−import qualified Filesystem.Path.CurrentOS as Path
46−import Formatting
47−import Gen.Formatting
4836import Gen.Solve
4937import Gen.Syntax
5038import Gen.Text
@@ -52,42 +40,43 @@ import Gen.Types
5240import HIndent
5341import Language.Haskell.Exts.Pretty
5442import Prelude hiding (sum)
55−import Text.EDE (Template)
5643
5744-- mapM isn't lazy here and I don't care!
58−flatten :: Map Ref (Fix Schema) -> Map Ref (Schema Ref)
45+flatten :: Map Id (Fix Schema) -> Map Id (Schema Id)
5946flatten m = execState (mapM_ (uncurry (prop Nothing)) (Map.toList m)) mempty
6047 where
6148 -- unfold the properties.
6249 prop p k (Fix f) = case f of
6350 Obj i ps -> do
64− o <- Obj i <$> Map.traverseWithKey (prop (Just ref)) ps
51+ o <- Obj i <$> Map.traverseWithKey (prop (Just this)) ps
6552 res o
6653
67− Arr i e -> do
68− e' <- prop (Just ref) "item" e
54+ Arr i (Fix e) -> do
55+ e' <- prop (Just this) "item" (Fix (e & required .~ True))
6956 res (Arr i e')
7057
7158 Enum i vs ds -> res (Enum i vs ds)
7259 Lit i l -> res (Lit i l)
73− Ref r -> pure r
74− Any' -> res Any'
60+ Ref _ r -> pure r
61+ Any i -> res (Any i)
7562
7663 where
7764 res x = do
78− m <- gets (Map.lookup ref)
79− case m of
80− Nothing -> modify (Map.insert ref x) >> pure ref
65+ s <- gets (Map.lookup this)
66+ case s of
67+ Nothing -> modify (Map.insert this x) >> pure this
8168 -- FIXME:
82− Just e -> error $ "Already exists: " ++ show (ref, e, x)
69+ Just e -> error $ "Already exists: " ++ show (this, e, x)
8370
84− ref = maybe k (<> k) p
71+ this = maybe k (<> k) p
8572
8673merge :: Versions -> [Service Data] -> [Library]
8774merge v = map go . groupBy (on (==) _svcLibrary)
8875 where
8976 go [x] = mk x
9077 go (x:xs) = (mk x) { _libServices = x :| xs }
78+ -- FIXME:
79+ go [] = error "Empty merge set!"
9180
9281 mk x = Library
9382 { _libName = _svcLibrary x
@@ -103,42 +92,32 @@ render s = runAST (Memo mempty mempty ss) $ do
10392 where
10493 ss = flatten (_svcSchemas s)
10594
106− go :: Ref -> Schema Ref -> AST (Maybe Data)
95+ go :: Id -> Schema Id -> AST (Maybe Data)
10796 go k = \case
10897 Arr {} -> pure Nothing
10998 Ref {} -> pure Nothing
110− Any' {} -> pure Nothing
99+ Any {} -> pure Nothing
111100 Lit {} -> pure Nothing
112101 Enum {} -> Just <$> sum
113102 where
114103 sum = Sum "sum help"
115104 <$> pp Indent enumDecl
116105
117− Obj _ rs -> Just <$> prod
106+ Obj _ rs -> Just <$> (traverse solve rs >>= prod)
118107 where
119− prod = Prod
120− <$> (objDecl k rs >>= pp Indent)
121− <*> ctor
122− <*> traverse (uncurry lens) (Map.toList rs)
108+ prod ts = Prod
109+ <$> (objDecl k ts >>= pp Indent)
110+ <*> ctor ts
111+ <*> traverse (uncurry lens) (Map.toList ts)
123112
124− ctor = Fun' "ctor help"
125− <$> pp None ctorSig
126− <*> pp Indent ctorDecl
113+ ctor ts = Fun' "ctor help"
114+ <$> (pp None (ctorSig k ts) >>= comments ts)
115+ <*> pp Indent (ctorDecl k ts)
127116
128− lens n v = Fun' "lens help"
117+ lens _ _ = Fun' "lens help"
129118 <$> pp None lensSig
130119 <*> pp Print lensDecl
131120
132− -- record declaration
133− -- smart constructor
134− -- signature
135− -- declaration
136− -- documentation
137− -- lenses per property
138− -- signature
139− -- declaration
140− -- documentation
141−
142121data PP
143122 = Indent
144123 | Print
@@ -152,7 +131,7 @@ pp i x
152131 where
153132 result = hoistEither . bimap e Build.toLazyText
154133
155− e = flip mappend (", when formatting datatype: " <> p) . LText.pack
134+ e = flip mappend (", when formatting: " <> p) . LText.pack
156135
157136 p = LText.dropWhile isSpace
158137 . LText.pack
@@ -170,3 +149,19 @@ pp i x
170149 { layout = PPNoLayout
171150 , spacing = False
172151 }
152+
153+-- FIXME: dirty hack to render smart ctor parameter comments.
154+comments :: Map Id Solved -> Rendered -> AST Rendered
155+comments (Map.toList -> rs) x = do
156+ let ks = filter (parameter . _schema . snd) rs
157+ ps = map (Just . fst) ks ++ repeat Nothing
158+ return
159+ . LText.replace " :: " "\n :: "
160+ . LText.intercalate "\n -> "
161+ . zipWith rel ps
162+ . map LText.strip
163+ $ LText.splitOn "->" x
164+ where
165+ rel Nothing t = t
166+ rel (Just k) t =
167+ t <> " -- ^ '" <> LText.fromStrict (idToText k) <> "'"
gen/src/Gen/Formatting.hs+1−1
@@ -17,7 +17,7 @@ module Gen.Formatting
1717import Control.Monad.Except
1818import qualified Data.Text.Lazy as LText
1919import qualified Data.Text.Lazy.Builder as Build
20−import Formatting hiding (left, right)
20+import Formatting hiding (base, left, right)
2121import Formatting.Internal (runFormat)
2222import Formatting.Time hiding (fmt)
2323import Gen.Types
gen/src/Gen/Solve.hs+41−48
@@ -21,40 +21,21 @@ module Gen.Solve where
2121
2222import Control.Applicative
2323import Control.Error
24−import Control.Lens
24+import Control.Lens hiding (enum)
2525import Control.Monad.Except
26−import Control.Monad.Morph
2726import Control.Monad.State
28−import qualified Data.Attoparsec.Text as A
29−import Data.Char
30−import Data.Function (on)
31−import qualified Data.HashMap.Strict as Map
32−import Data.List (groupBy, intersect, nub, sort)
33−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
34−import qualified Data.List.NonEmpty as NE
35−import Data.Ord
36−import Data.Semigroup hiding (Sum)
37−import Data.Text (Text)
38−import qualified Data.Text as Text
39−import qualified Data.Text.Lazy as LText
40−import qualified Data.Text.Lazy.Builder as Build
41−import Data.Text.Manipulate
42−import Debug.Trace
43−import qualified Filesystem.Path.CurrentOS as Path
44−import Formatting
27+import qualified Data.HashMap.Strict as Map
28+import Data.List (intersect)
29+import Data.Semigroup ((<>))
4530import Gen.Formatting
46−import Gen.Text
4731import Gen.Types
48−import HIndent
49−import Language.Haskell.Exts.Pretty
50−import Prelude hiding (sum)
51−import Text.EDE (Template)
32+import Prelude hiding (sum)
5233
5334-- Just use hashmaps to lookup refs etc, and hide all the mutation
5435-- behind a StateT interface.
5536
5637data TType
57− = TType Ref
38+ = TType Id
5839 | TLit Lit
5940 | TMaybe TType
6041 | TEither TType TType
@@ -77,28 +58,37 @@ data Derive
7758 deriving (Eq, Show)
7859
7960data Memo = Memo
80− { _typed :: Map Ref TType
81− , _derived :: Map Ref [Derive]
82− , _schemas :: Map Ref (Schema Ref)
61+ { _typed :: Map Id TType
62+ , _derived :: Map Id [Derive]
63+ , _schemas :: Map Id (Schema Id)
8364 }
8465
8566makeLenses ''Memo
8667
8768type AST = ExceptT Error (State Memo)
8869
89−type Solved = (Schema Ref, TType, [Derive])
70+data Solved = Solved
71+ { _schema :: Schema Id
72+ , _type :: TType
73+ , _deriving :: [Derive]
74+ }
75+
76+instance HasInfo Solved where
77+ info = f . info
78+ where
79+ f = lens _schema (\s a -> s { _schema = a })
9080
9181runAST :: Memo -> AST a -> Either Error a
9282runAST s m = evalState (runExceptT m) s
9383
94−schema :: Ref -> AST (Schema Ref)
84+schema :: Id -> AST (Schema Id)
9585schema k = do
9686 m <- gets (Map.lookup k . view schemas)
9787 case m of
98− Nothing -> failure ("Missing Schema: " % fref) k
88+ Nothing -> failure ("Missing Schema: " % fid) k
9989 Just v -> pure v
10090
101−memo :: Lens' Memo (Map Ref v) -> Ref -> (Schema Ref -> AST v) -> AST v
91+memo :: Lens' Memo (Map Id v) -> Id -> (Schema Id -> AST v) -> AST v
10292memo l k f = do
10393 m <- gets (Map.lookup k . view l)
10494 case m of
@@ -108,30 +98,33 @@ memo l k f = do
10898 l %= Map.insert k x
10999 return x
110100
111−solve :: Ref -> AST Solved
112−solve k = (,,) <$> schema k <*> typeOf k <*> derive k
101+solve :: Id -> AST Solved
102+solve k = Solved <$> schema k <*> typeOf k <*> derive k
113103
114−typeOf :: Ref -> AST TType
104+typeOf :: Id -> AST TType
115105typeOf k = loc "typeOf" k $ memo typed k go
116106 where
117− go = \case
107+ go s = fmap may $ case s of
118108 Obj {} -> pure (TType k)
119− Arr _ r -> pure (TList (TType r))
109+ Arr _ r -> TList <$> typeOf r
120110 Enum {} -> pure (TType k)
121− Ref r -> typeOf r
122− Any' -> pure (TEither (TLit Text) (TLit Long))
111+ Ref _ r -> typeOf r
112+ Any {} -> pure (TEither (TLit Text) (TLit Long))
123113 Lit _ l -> pure (TLit l)
114+ where
115+ may | parameter s = id
116+ | otherwise = TMaybe
124117
125−derive :: Ref -> AST [Derive]
118+derive :: Id -> AST [Derive]
126119derive k = loc "derive" k $ memo derived k go
127120 where
128121 go = \case
129− Obj _ rs -> foldM props base (Map.elems rs)
130− Arr _ r -> pure [] -- mappend monoid . intersect base <$> derive r
131− Enum {} -> pure enum
132− Ref r -> derive r
133− Any' -> pure base
134− Lit _ l -> pure $
122+ Obj _ rs -> foldM props base (Map.elems rs)
123+ Arr _ r -> mappend monoid . intersect base <$> derive r
124+ Enum {} -> pure enum
125+ Ref _ r -> derive r
126+ Any _ -> pure base
127+ Lit _ l -> pure $
135128 case l of
136129 Text -> base <> [DOrd, DIsString]
137130 Bool -> base <> enum
@@ -146,5 +139,5 @@ derive k = loc "derive" k $ memo derived k go
146139 enum = [DOrd, DEnum]
147140 base = [DEq, DRead, DShow, DData, DTypeable, DGeneric]
148141
149−loc :: String -> Ref -> a -> a
150−loc n r = id -- trace (n ++ ": " ++ Text.unpack (refToText r))
142+loc :: String -> Id -> a -> a
143+loc _ _ = id -- trace (n ++ ": " ++ Text.unpack (refToText r))
gen/src/Gen/Syntax.hs+57−32
@@ -11,50 +11,64 @@
1111
1212module Gen.Syntax where
1313
14+import Control.Lens hiding (strict)
15+import Data.Foldable (foldr')
1416import qualified Data.HashMap.Strict as Map
15−import Data.Semigroup
16−import Data.String
1717import Data.Text (Text)
1818import qualified Data.Text as Text
1919import Data.Text.Manipulate
2020import Gen.Solve
21+import Gen.Text
2122import Gen.Types
22−import qualified Language.Haskell.Exts as Exts
2323import Language.Haskell.Exts.Build
2424import Language.Haskell.Exts.SrcLoc
2525import Language.Haskell.Exts.Syntax hiding (Int)
2626
27−objDecl :: Ref -> Map Ref Ref -> AST Decl
28−objDecl r rs = do
29− con <- conDecl n rs
30− ds <- derivings r
31− return $! DataDecl noLoc arity [] n [] [con] ds
27+objDecl :: Id -> Map Id Solved -> AST Decl
28+objDecl r rs = DataDecl noLoc arity [] d [] [conDecl d rs] <$> derivings r
3229 where
33− n = dname r
30+ d = dname r
3431
3532 arity | Map.size rs == 1 = NewType
3633 | otherwise = DataType
3734
38−conDecl :: Name -> Map Ref Ref -> AST QualConDecl
39−conDecl n rs = QualConDecl noLoc [] [] <$> body
35+conDecl :: Name -> Map Id Solved -> QualConDecl
36+conDecl n rs = QualConDecl noLoc [] [] body
4037 where
4138 body = case Map.toList rs of
42− [] -> pure (ConDecl n [])
43− [x] -> RecDecl n . (:[]) <$> field internal x
44− xs -> RecDecl n <$> traverse (field (strict . internal)) xs
39+ [] -> ConDecl n []
40+ [x] -> RecDecl n [field internal x]
41+ xs -> RecDecl n (map (field (strict . internal)) xs)
4542
46− field f (k, v) = do
47− t <- typeOf v
48− return ([fname k], f t)
43+ field f (k, v) = ([fname k], f (_type v))
4944
50−derivings :: Ref -> AST [Deriving]
51−derivings = fmap (map ((,[]) . unqual . drop 1 . show)) . derive
45+ctorSig :: Id -> Map Id Solved -> Decl
46+ctorSig r rs = TypeSig noLoc [c] ts
47+ where
48+ c = cname r
49+ d = dname r
50+
51+ ts = foldr' TyFun (TyCon (UnQual d)) ps
52+ ps = parameters (Map.elems rs)
53+
54+ctorDecl :: Id -> Map Id Solved -> Decl
55+ctorDecl r rs = sfun noLoc c ps (UnGuardedRhs rhs) noBinds
56+ where
57+ c = cname r
58+ d = dname r
59+
60+ rhs | Map.null rs = var d
61+ | otherwise = RecConstr (UnQual d) $
62+ map (uncurry fieldUpdate) (Map.toList rs)
5263
53−ctorSig :: Decl
54−ctorSig = emptyDecl
64+ ps = map pname . Map.keys $ Map.filter (view required) rs
5565
56−ctorDecl :: Decl
57−ctorDecl = emptyDecl
66+fieldUpdate :: Id -> Solved -> FieldUpdate
67+fieldUpdate r s = FieldUpdate (UnQual (fname r)) rhs
68+ where
69+ rhs | Just x <- s ^. defaulted = str x
70+ | s ^. required = var (pname r)
71+ | otherwise = var (name "Nothing")
5872
5973lensSig :: Decl
6074lensSig = emptyDecl
@@ -68,13 +82,20 @@ enumDecl = emptyDecl
6882emptyDecl :: Decl
6983emptyDecl = DataDecl noLoc DataType [] (name "Empty") [] [] []
7084
71−internal :: TType -> Type
85+derivings :: Id -> AST [Deriving]
86+derivings = fmap (map ((,[]) . unqual . drop 1 . show)) . derive
87+
88+parameters :: [Solved] -> [Type]
89+parameters = map (external . _type) . filter (view required)
90+
91+external, internal :: TType -> Type
92+external = internal
7293internal = go
7394 where
7495 go = \case
7596 TType r -> tycon r
7697 TMaybe t -> TyApp (tycon "Maybe") (go t)
77−-- TEither a b ->
98+ TEither a b -> TyApp (TyApp (tycon "Either") (go a)) (go b)
7899 TList t -> TyList (go t)
79100 TLit l -> lit l
80101
@@ -91,17 +112,21 @@ strict = TyBang BangedTy . \case
91112 t@TyApp{} -> TyParen t
92113 t -> t
93114
94−dname :: Ref -> Name
115+dname, cname, fname, lname, pname :: Id -> Name
95116dname = name . ref upperHead
117+cname = name . ref (renameReserved . lowerHead)
118+fname = name . ref (Text.cons '_' . lowerHead)
119+lname = name . ref lowerHead
120+pname = name . ref (flip Text.snoc '_' . Text.cons 'p' . upperHead)
96121
97−fname :: Ref -> Name
98−fname = name . ref (Text.cons '_')
99−
100−tycon :: Ref -> Type
122+tycon :: Id -> Type
101123tycon = TyCon . unqual . ref upperHead
102124
103−ref :: (Text -> Text) -> Ref -> String
104−ref f = Text.unpack . f . refToText
125+ref :: (Text -> Text) -> Id -> String
126+ref f = Text.unpack . f . idToText
105127
106128unqual :: String -> QName
107129unqual = UnQual . name
130+
131+str :: Text -> Exp
132+str = strE . Text.unpack
gen/src/Gen/Text.hs+18−23
@@ -27,8 +27,8 @@ import qualified Data.Text as Text
2727-- import Data.Text.ICU.Replace (Replace)
2828-- import qualified Data.Text.ICU.Replace as RE
2929import Data.Text.Manipulate
30−-- import Text.Parsec.Language (haskellDef)
31−-- import Text.Parsec.Token (reservedNames)
30+import Text.Parsec.Language (haskellDef)
31+import Text.Parsec.Token (reservedNames)
3232
3333-- asText :: (Text -> Text) -> String -> String
3434-- asText f = Text.unpack . f . Text.pack
@@ -68,27 +68,22 @@ renameAbbrev = mconcat
6868 . stripPrefix "Google "
6969 . stripSuffix " API"
7070
71−-- renameReserved :: Text -> Text
72−-- renameReserved x
73−-- | x `Set.member` xs = x <> "'"
74−-- | otherwise = x
75−-- where
76−-- xs = Set.fromList $
77−-- [ "head"
78−-- , "tail"
79−-- , "delete"
80−-- , "filter"
81−-- , "True"
82−-- , "False"
83−-- , "map"
84−-- , "object"
85−-- , "get"
86−-- , "group"
87−-- , "GT"
88−-- , "LT"
89−-- , "EQ"
90−-- , "Error"
91−-- ] ++ map Text.pack (reservedNames haskellDef)
71+renameReserved :: Text -> Text
72+renameReserved x
73+ | x `Set.member` xs = x <> "'"
74+ | otherwise = x
75+ where
76+ xs = Set.fromList $
77+ [ "head"
78+ , "tail"
79+ , "delete"
80+ , "filter"
81+ , "map"
82+ , "object"
83+ , "get"
84+ , "group"
85+ , "error"
86+ ] ++ map Text.pack (reservedNames haskellDef)
9287
9388-- camelAcronym :: Text -> Text
9489-- camelAcronym x = replaceAll x xs
gen/src/Gen/Tree.hs+2−5
@@ -22,13 +22,10 @@ module Gen.Tree
2222 ) where
2323
2424import Control.Error
25−import Control.Lens (each, (^.), (^..))
2625import Control.Monad
2726import Control.Monad.Except
2827import Data.Aeson hiding (json)
2928import Data.Bifunctor
30−import Data.Functor.Identity
31−import qualified Data.HashMap.Strict as Map
3229import qualified Data.List.NonEmpty as NE
3330import Data.Monoid
3431import Data.Text (Text)
@@ -99,8 +96,8 @@ populate d Templates{..} l = (encodeString d :/) . dir lib <$> layout
9996 lib :: Path
10097 lib = fromText (_libName l)
10198
102− mod :: NS -> [NS] -> Template -> DirTree (Either Error Touch)
103− mod n is t = write . module' n is t . pure $ toJSON l
99+ -- mod :: NS -> [NS] -> Template -> DirTree (Either Error Touch)
100+ -- mod n is t = write . module' n is t . pure $ toJSON l
104101
105102 file :: Path -> Template -> DirTree (Either Error Touch)
106103 file p t = write $ file' p t (pure env)
gen/src/Gen/Types.hs+51−64
@@ -26,32 +26,29 @@ module Gen.Types
2626 ( module Gen.Types
2727 , module Gen.Types.Map
2828 , module Gen.Types.NS
29+ , module Gen.Types.Id
2930 ) where
3031
3132import Control.Applicative
33+import Control.Lens hiding ((.=))
3234import Data.Aeson hiding (Bool, String)
3335import Data.Aeson.TH
34−import qualified Data.Attoparsec.Text as A
35−import Data.Bifunctor
3636import Data.Char
3737import Data.Function (on)
38−import Data.Hashable
39−import qualified Data.HashMap.Strict as Map
40−import Data.List (groupBy, nub, sort)
41−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
38+import Data.List.NonEmpty (NonEmpty (..))
4239import qualified Data.List.NonEmpty as NE
40+import Data.Maybe
4341import Data.Ord
44−import Data.Semigroup hiding (Sum)
45−import Data.String
42+import Data.Semigroup ((<>))
4643import Data.Text (Text)
4744import qualified Data.Text as Text
4845import qualified Data.Text.Lazy as LText
4946import qualified Data.Text.Lazy.Builder as Build
50−import Data.Text.Manipulate
5147import qualified Data.Text.Read as Read
5248import qualified Filesystem.Path.CurrentOS as Path
5349import Formatting
5450import Gen.Text
51+import Gen.Types.Id
5552import Gen.Types.Map
5653import Gen.Types.NS
5754import GHC.Generics
@@ -144,10 +141,10 @@ data Location
144141deriveJSON (js "") ''Location
145142
146143data Info = Info
147− { _infoDescription :: Maybe Text
148− , _infoDefault :: Maybe Text
149− , _infoRequired :: !Bool
150− , _infoLocation :: Maybe Location
144+ { _description :: Maybe Text
145+ , _defaulted :: Maybe Text
146+ , _required :: !Bool
147+ , _location :: Maybe Location
151148 } deriving (Eq, Show)
152149
153150instance FromJSON Info where
@@ -159,6 +156,11 @@ instance FromJSON Info where
159156
160157deriveToJSON (js "_info") ''Info
161158
159+makeClassy ''Info
160+
161+parameter :: HasInfo a => a -> Bool
162+parameter s = s ^. required && isNothing (s ^. defaulted)
163+
162164data Lit
163165 = Text
164166 | Bool
@@ -170,7 +172,7 @@ data Lit
170172
171173instance FromJSON Lit where
172174 parseJSON = withObject "literal" $ \o -> do
173− n <- min =<< (o .:? "minimum" .!= "0")
175+ n <- num =<< (o .:? "minimum" .!= "0")
174176 (o .: "format" <|> o .: "type") >>= \case
175177 -- Types
176178 "string" -> pure Text
@@ -182,7 +184,7 @@ instance FromJSON Lit where
182184 e -> fail $
183185 "Unable to parse Lit from " ++ Text.unpack e
184186 where
185− min = either fail (pure . fst) . Read.decimal
187+ num = either fail (pure . fst) . Read.decimal
186188
187189 nat :: Int -> Lit -> Lit
188190 nat n | n > 0 = const Nat
@@ -196,65 +198,50 @@ instance FromJSON Lit where
196198
197199deriveToJSON (js "") ''Lit
198200
199−data Ref
200− = Opaque [Text]
201− | Direct Text
202− deriving (Eq, Show, Generic)
203−
204−instance Semigroup Ref where
205− a <> b = case (a, b) of
206− (Opaque xs, Opaque ys) -> Opaque (xs <> ys)
207− (Opaque xs, Direct y) -> Opaque (xs <> [y])
208− (Direct x, Opaque ys) -> Opaque (x : ys)
209− (Direct x, Direct y) -> Opaque [x, y]
210−
211−instance Hashable Ref
212−
213−instance IsString Ref where
214− fromString = Direct . fromString
215−
216−instance FromJSON Ref where parseJSON = withText "ref" (pure . Direct)
217−instance ToJSON Ref where toJSON = toJSON . refToText
218−
219−instance FromJSON a => FromJSON (Map Ref a) where
220− parseJSON = fmap (Map.fromList . map (first Direct) . Map.toList) . parseJSON
221−
222−instance ToJSON a => ToJSON (Map Ref a) where
223− toJSON = toJSON . Map.fromList . map (first refToText) . Map.toList
224−
225−refToText :: Ref -> Text
226−refToText = \case
227− Opaque xs -> mconcat (map upperHead xs)
228− Direct n -> n
229−
230−fref :: Format a (Ref -> a)
231−fref = later (Build.fromText . refToText)
232−
233201data Schema a
234− = Obj Info (Map Ref a)
202+ = Obj Info (Map Id a)
235203 | Arr Info a
236204 | Enum Info [Text] [Text]
237205 | Lit Info Lit
238− | Ref Ref
239− | Any' -- String or Number
206+ | Ref Info Id
207+ | Any Info -- String or Number
240208 deriving (Eq, Show, Functor, Foldable, Traversable)
241209
242210instance FromJSON (Fix Schema) where
243− parseJSON = withObject "schema" $ \o -> Fix <$> (ref o <|> schema o)
211+ parseJSON = withObject "schema" $ \o -> do
212+ i <- parseJSON (Object o)
213+ Fix <$> (ref i o <|> schema i o)
244214 where
245− schema o = do
246− i <- parseJSON (Object o)
247− o .: "type" >>= \case
248− "object" -> Obj i <$> o .: "properties"
249− "array" -> Arr i <$> o .: "items"
250− "any" -> pure Any'
251− _ -> enum i o <|> Lit i <$> parseJSON (Object o)
215+ schema i o = o .: "type" >>= \case
216+ "object" -> Obj i <$> o .: "properties"
217+ "array" -> Arr i <$> o .: "items"
218+ "any" -> pure (Any i)
219+ _ -> enm i o <|> Lit i <$> parseJSON (Object o)
252220
253− enum i o = Enum i <$> o .: "enum" <*> o .: "enumDescriptions"
254− ref o = Ref <$> o .: "$ref"
221+ enm i o = Enum i <$> o .: "enum" <*> o .: "enumDescriptions"
222+ ref i o = Ref i <$> o .: "$ref"
255223
256224deriveToJSON (js "") ''Schema
257225
226+instance HasInfo (Schema a) where
227+ info = lens f (flip g)
228+ where
229+ f = \case
230+ Obj i _ -> i
231+ Arr i _ -> i
232+ Enum i _ _ -> i
233+ Lit i _ -> i
234+ Ref i _ -> i
235+ Any i -> i
236+
237+ g i = \case
238+ Obj _ p -> Obj i p
239+ Arr _ e -> Arr i e
240+ Enum _ v d -> Enum i v d
241+ Lit _ l -> Lit i l
242+ Ref _ r -> Ref i r
243+ Any {} -> Any i
244+
258245data Param = Param Location -- (Fix Schema)
259246
260247instance FromJSON Param where
@@ -309,8 +296,8 @@ data Service a = Service
309296 , _svcServicePath :: Text
310297 , _svcBatchPath :: Text
311298 , _svcAuth :: Maybe Object
312− , _svcParameters :: Map Ref Param
313− , _svcSchemas :: Map Ref a
299+ , _svcParameters :: Map Id Param
300+ , _svcSchemas :: Map Id a
314301 , _svcResources :: Object
315302 } deriving (Generic)
316303
gen/src/Gen/Types/Id.hsnew file+79
@@ -0,0 +1,79 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE LambdaCase #-}
4+
5+-- Module : Gen.Types.Id
6+-- Copyright : (c) 2015 Brendan Hay
7+-- License : Mozilla Public License, v. 2.0.
8+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
9+-- Stability : provisional
10+-- Portability : non-portable (GHC extensions)
11+
12+module Gen.Types.Id
13+ ( Id
14+ , idToText
15+ , idFromText
16+ , fid
17+ ) where
18+
19+import Control.Applicative
20+import Control.Lens hiding ((.=))
21+import Data.Aeson hiding (Bool, String)
22+import Data.Aeson.TH
23+import qualified Data.Attoparsec.Text as A
24+import Data.Bifunctor
25+import Data.Char
26+import Data.Function (on)
27+import Data.Hashable
28+import qualified Data.HashMap.Strict as Map
29+import Data.Maybe
30+import Data.Ord
31+import Data.Semigroup hiding (Sum)
32+import Data.String
33+import Data.Text (Text)
34+import qualified Data.Text as Text
35+import qualified Data.Text.Lazy as LText
36+import qualified Data.Text.Lazy.Builder as Build
37+import Data.Text.Manipulate
38+import Formatting
39+import Gen.Text
40+import Gen.Types.Map
41+import GHC.Generics
42+
43+data Id
44+ = Opaque [Text]
45+ | Direct Text
46+ deriving (Eq, Show, Generic)
47+
48+instance Semigroup Id where
49+ a <> b = case (a, b) of
50+ (Opaque xs, Opaque ys) -> Opaque (xs <> ys)
51+ (Opaque xs, Direct y) -> Opaque (xs <> [y])
52+ (Direct x, Opaque ys) -> Opaque (x : ys)
53+ (Direct x, Direct y) -> Opaque [x, y]
54+
55+instance IsString Id where
56+ fromString = idFromText . fromString
57+
58+instance Hashable Id
59+
60+instance FromJSON Id where parseJSON = withText "id" (pure . idFromText)
61+instance ToJSON Id where toJSON = toJSON . idToText
62+
63+instance FromJSON a => FromJSON (Map Id a) where
64+ parseJSON o = Map.fromList . map (first idFromText) . Map.toList
65+ <$> parseJSON o
66+
67+instance ToJSON a => ToJSON (Map Id a) where
68+ toJSON = toJSON . Map.fromList . map (first idToText) . Map.toList
69+
70+idToText :: Id -> Text
71+idToText = \case
72+ Opaque xs -> mconcat (map upperHead xs)
73+ Direct n -> n
74+
75+idFromText :: Text -> Id
76+idFromText = Direct . renameReserved
77+
78+fid :: Format a (Id -> a)
79+fid = later (Build.fromText . idToText)
gen/src/Main.hs+6−1
@@ -28,7 +28,7 @@ import Gen.Formatting
2828import Gen.IO
2929import qualified Gen.JSON as JS
3030import qualified Gen.Tree as Tree
31−import Gen.Types
31+import Gen.Types hiding (info)
3232import Options.Applicative
3333
3434data Opt = Opt
@@ -182,6 +182,11 @@ class HasName a where getName :: a -> Text
182182instance HasName Spec where getName = _specName
183183instance HasName Library where getName = _libName
184184
185+counter :: (MonadIO m, HasName a)
186+ => Text
187+ -> [a]
188+ -> (a -> ExceptT Error m b)
189+ -> ExceptT Error m [b]
185190counter k xs f = forM (zip [1..] xs) $ \(n, x) -> do
186191 title ("[" % int % "/" % int % "] " % stext % ":" % stext)
187192 (n :: Int) i k (getName x)