@@ -6,15 +6,16 @@
6{-# LANGUAGE ExtendedDefaultRules #-}
6{-# LANGUAGE ExtendedDefaultRules #-}
7{-# LANGUAGE FlexibleContexts #-}
7{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE FlexibleInstances #-}
9+{-# LANGUAGE FunctionalDependencies #-}
9{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10{-# LANGUAGE KindSignatures #-}
11{-# LANGUAGE KindSignatures #-}
11{-# LANGUAGE LambdaCase #-}
12{-# LANGUAGE LambdaCase #-}
13+{-# LANGUAGE MultiParamTypeClasses #-}
12{-# LANGUAGE OverloadedStrings #-}
14{-# LANGUAGE OverloadedStrings #-}
15+{-# LANGUAGE RankNTypes #-}
13{-# LANGUAGE RecordWildCards #-}
16{-# LANGUAGE RecordWildCards #-}
14{-# LANGUAGE TemplateHaskell #-}
17{-# LANGUAGE TemplateHaskell #-}
16−{-# OPTIONS_GHC -fno-warn-type-defaults #-}
18-- Module : Gen.Types
19-- Module : Gen.Types
19-- Copyright : (c) 2015 Brendan Hay
20-- Copyright : (c) 2015 Brendan Hay
20-- License : Mozilla Public License, v. 2.0.
21-- License : Mozilla Public License, v. 2.0.
@@ -28,12 +29,19 @@ module Gen.Types
28 , module Gen.Types.Id
29 , module Gen.Types.Id
29 , module Gen.Types.Map
30 , module Gen.Types.Map
30 , module Gen.Types.NS
31 , module Gen.Types.NS
32+ , module Gen.Types.Schema
33+ , module Gen.Types.Data
33import Control.Applicative
36import Control.Applicative
34import Control.Lens hiding ((.=))
37import Control.Lens hiding ((.=))
38+import Control.Monad.Except
39+import Control.Monad.State.Strict
35−import Data.Aeson hiding (Bool, String)
40+import Data.Aeson hiding (Array, Bool, String)
41+import qualified Data.Aeson as A
36import Data.Aeson.TH
42import Data.Aeson.TH
43+import Data.CaseInsensitive (CI)
44+import qualified Data.CaseInsensitive as CI
37import Data.Char
45import Data.Char
38import Data.Function (on)
46import Data.Function (on)
39import qualified Data.HashMap.Strict as Map
47import qualified Data.HashMap.Strict as Map
@@ -54,37 +62,23 @@ import qualified Filesystem.Path.CurrentOS as Path
54import Formatting
62import Formatting
55import Gen.Orphans ()
63import Gen.Orphans ()
56import Gen.Text
64import Gen.Text
57import Gen.Types.Help
67import Gen.Types.Help
58import Gen.Types.Id
68import Gen.Types.Id
59import Gen.Types.Map
69import Gen.Types.Map
60import Gen.Types.NS
70import Gen.Types.NS
71+import Gen.Types.Schema
61import GHC.Generics
72import GHC.Generics
62import GHC.TypeLits
73import GHC.TypeLits
63import Language.Haskell.Exts (Name)
74import Language.Haskell.Exts (Name)
64import Language.Haskell.Exts.Pretty (Pretty, prettyPrint)
75import Language.Haskell.Exts.Pretty (Pretty, prettyPrint)
76+import Prelude hiding (Enum)
65import Text.EDE (Template)
77import Text.EDE (Template)
69−newtype Fix f = Fix (f (Fix f))
71−type Set = Set.HashSet
79+type Set = Set.HashSet
72−type Error = LText.Text
80+type Error = LText.Text
73−type Rendered = LText.Text
74−type Path = Path.FilePath
81+type Path = Path.FilePath
76−toTextIgnore :: Path -> Text
77−toTextIgnore = either id id . Path.toText
79−data Templates = Templates
80− { cabalTemplate :: Template
81− , tocTemplate :: Template
82− , readmeTemplate :: Template
83− , typesTemplate :: Template
84− , prodTemplate :: Template
85− , sumTemplate :: Template
86− , actionTemplate :: Template
89newtype Version (v :: Symbol) = Version Text
83newtype Version (v :: Symbol) = Version Text
90 deriving (Eq, Show)
84 deriving (Eq, Show)
@@ -105,193 +99,118 @@ data Versions = Versions
105 , _coreVersion :: CoreVer
99 , _coreVersion :: CoreVer
106 } deriving (Show)
100 } deriving (Show)
102+makeClassy ''Versions
108−-- FIXME: need a more comprehensive 'vm_alpha' vs 'vm1.1' etc check.
104+-- FIXME: need a more comprehensive 'vm_alpha' vs 'vm1.1' version check.
109−data Spec = Spec
105+data Model = Model
110− { _specName :: Text
106+ { modelName :: Text
111− , _specPrefix :: Text
107+ , modelPrefix :: Text
112− , _specVersion :: Text
108+ , modelVersion :: Text
113− , _specPath :: Path
109+ , modelPath :: Path
116−instance Eq Spec where
112+instance Eq Model where
117− (==) = on (==) _specPrefix
113+ (==) = on (==) modelPrefix
119−instance Ord Spec where
115+instance Ord Model where
120 compare a b =
116 compare a b =
121− on compare _specPrefix a b
117+ on compare modelPrefix a b
122− <> on compare (Down . _specVersion) a b
118+ <> on compare (Down . modelVersion) a b
124−specFromPath :: Path -> Spec
120+modelFromPath :: Path -> Model
125−specFromPath x = Spec n p v x
121+modelFromPath x = Model n p v x
128− n = Text.init
123+ n = Text.init
129− . Text.intercalate "/"
124+ . Text.intercalate "/"
130− . drop 1
125+ . drop 1
131− . dropWhile (/= "model")
126+ . dropWhile (/= "model")
132− $ Text.split (== '/') p
127+ $ Text.split (== '/') p
134− p = toTextIgnore (Path.parent (Path.parent x))
129+ p = toTextIgnore (Path.parent (Path.parent x))
135− v = toTextIgnore (Path.dirname x)
130+ v = toTextIgnore (Path.dirname x)
137−data Protocol = REST
132+data Templates = Templates
138− deriving (Eq, Show)
133+ { cabalTemplate :: Template
134+ , tocTemplate :: Template
135+ , readmeTemplate :: Template
136+ , typesTemplate :: Template
137+ , prodTemplate :: Template
138+ , sumTemplate :: Template
139+ , actionTemplate :: Template
140−instance FromJSON Protocol where
142+tocImports, typeImports, prodImports, sumImports, actionImports
141− parseJSON = withText "protocol" $ \case
143+ :: Service a -> [NS]
142− "rest" -> pure REST
144+tocImports _ = [preludeNS]
143− e -> fail $ "Unable to parse protocol from " ++ Text.unpack e
145+typeImports s = sort [preludeNS, prodNS s, sumNS s]
146+prodImports s = sort [preludeNS, sumNS s]
147+sumImports _ = [preludeNS]
148+actionImports s = sort [preludeNS, typesNS s]
150+tocNS, typesNS, prodNS, sumNS :: Service a -> NS
151+tocNS = mappend "Network.Google" . mkNS . _sCanonicalName
152+typesNS = (<> "Types") . tocNS
153+prodNS = (<> "Product") . typesNS
154+sumNS = (<> "Sum") . typesNS
145−instance ToJSON Protocol where
156+preludeNS :: NS
146− toJSON = toJSON . map toLower . show
157+preludeNS = "Network.Google.Prelude"
148−data Location
159+-- actionNS :: [Text] -> NS
149− = Query
160+-- actionNS = mappend (NS ["Network", "Google", "API"]) . NS
153−deriveJSON (js "") ''Location
162+-- exposedModules :: Service s p API -> [NS]
154−
163+-- exposedModules s = sort (tocNS s : typesNS s : map _actNS (svcActions s))
156− { _description :: Maybe Help
157− , _defaulted :: Maybe Text
158− , _required :: !Bool
159− , _location :: Maybe Location
160− } deriving (Eq, Show)
162−instance FromJSON Info where
163− parseJSON = withObject "info" $ \o -> Info
164− <$> o .:? "description"
166− <*> o .:? "required" .!= False
167− <*> o .:? "location"
169−deriveToJSON (js "_info") ''Info
173−parameter :: HasInfo a => a -> Bool
174−parameter s = s ^. required && isNothing (s ^. defaulted)
191−instance FromJSON Lit where
165+-- otherModules :: Service s p r -> [NS]
192− parseJSON = withObject "literal" $ \o -> do
166+-- otherModules s = sort [prodNS s, sumNS s]
193− n <- num =<< (o .:? "minimum" .!= "0")
167+
194− (o .: "format" <|> o .: "type") >>= \case
168+toTextIgnore :: Path -> Text
195− -- Types
169+toTextIgnore = either id id . Path.toText
196− "string" -> pure Text
170+
197− "boolean" -> pure Bool
171+data Library = Library
198− -- Formats
172+ { _lVersions :: Versions
199− "float" -> pure Float
173+ , _lService :: Service Global
200− "double" -> pure Double
174+ -- , _lMethods ::
201− "byte" -> pure (nat n Byte)
175+ -- , _lResources :: []
202− "uint32" -> pure (nat n UInt32)
176+ -- , _lSchemas :: [Data]
203− "uint64" -> pure (nat n UInt64)
177+ }
204− "int32" -> pure (nat n Int32)
205− "int64" -> pure (nat n Int64)
207− "date-time" -> pure Time
209− "Unable to parse Lit from " ++ Text.unpack e
211− num = either fail (pure . fst) . Read.decimal
213− nat :: Int -> Lit -> Lit
214− nat n | n > 0 = const Nat
217−-- "pattern": "[A-Z]{3}",
218−-- "pattern": "[0-9a-zA-Z,]+",
219−-- "pattern": "[a-zA-Z]+==[a-zA-Z0-9_+-]+",
221−-- "format": "date-time", "uint64", "int32",
223−deriveToJSON (js "") ''Lit
226− = Obj Info (Map Local a)
228− | Enum Info [Text] [Help]
231− | Any Info -- String or Number
232− deriving (Eq, Show, Functor, Foldable, Traversable)
234−instance FromJSON (Fix Schema) where
235− parseJSON = withObject "schema" $ \o -> do
236− i <- parseJSON (Object o)
237− Fix <$> (ref i o <|> schema i o)
239− schema i o = o .: "type" >>= \case
240− "object" -> Obj i <$> o .:? "properties" .!= mempty
241− "array" -> Arr i <$> o .: "items"
242− "any" -> pure (Any i)
243− _ -> enm i o <|> Lit i <$> parseJSON (Object o)
245− enm i o = Enum i <$> o .: "enum" <*> o .: "enumDescriptions"
179+makeLenses ''Library
246− ref i o = Ref i <$> o .: "$ref"
248−deriveToJSON (js "") ''Schema
181+instance HasVersions Library where
182+ versions = lVersions
184+instance HasDescription Library Global where
185+ description = lService . description
187+instance HasService Library Global where
190+instance ToJSON Library where
192+ [ "libraryName" .= (l ^. sLibrary)
193+ , "libraryTitle" .= (l ^. dTitle)
194+ , "libraryDescription" .= Desc 4 (l ^. dDescription)
195+ , "libraryVersion" .= (l ^. libraryVersion)
196+ , "coreVersion" .= (l ^. coreVersion)
197+ , "clientVersion" .= (l ^. clientVersion)
198+ -- , "exposedModules" .= concatMap exposedModules (NE.toList _libServices)
199+ -- , "otherModules" .= concatMap otherModules (NE.toList _libServices)
202+-- data Service = Service
203+-- { _sLibrary :: Text
204+-- , _sCanonicalName :: Text
205+-- , _sOwnerName :: Text
206+-- , _sOwnerDomain :: Text
207+-- , _sPackagePath :: Maybe Text
208+-- , _sDescription :: Description
209+-- } deriving (Eq, Show)
250−instance HasInfo (Schema a) where
251− info = lens f (flip g)
264− Enum _ v d -> Enum i v d
270− { _prmLocation :: !Location
271− , _prmRepeated :: !Bool
274− } deriving (Show, Functor, Foldable, Traversable)
276−-- paramSchema :: Param -> Fix Schema
278−-- | _prmRepeated p = Fix $ Arr (_prmInfo p) lit
281−-- lit = Fix $ Lit (_prmInfo p) (_prmLiteral p)
283−instance FromJSON a => FromJSON (Param a) where
284− parseJSON = withObject "parameter" $ \o -> Param
286− <*> o .:? "repeated" .!= False
287− <*> parseJSON (Object o)
288− <*> parseJSON (Object o)
290−instance HasInfo (Param a) where
291− info = lens _prmInfo (\s a -> s { _prmInfo = a })
293data TType
212data TType
294− = TType Id
213+ = TType Global
295 | TLit Lit
214 | TLit Lit
296 | TMaybe TType
215 | TMaybe TType
297 | TEither TType TType
216 | TEither TType TType
@@ -314,9 +233,9 @@ data Derive
314 deriving (Eq, Show)
233 deriving (Eq, Show)
316data Solved = Solved
235data Solved = Solved
317− { _ident :: Id
236+ { _ident :: Global
318− , _prefix :: Pre
237+ , _prefix :: Prefix
319− , _schema :: Schema Id
238+ , _schema :: Schema Global
320 , _type :: TType
239 , _type :: TType
321 , _deriving :: [Derive]
240 , _deriving :: [Derive]
@@ -329,254 +248,24 @@ instance HasInfo Solved where
329monoid :: Solved -> Bool
248monoid :: Solved -> Bool
330monoid = elem DMonoid . _deriving
249monoid = elem DMonoid . _deriving
332−data Syn a = Syn { syntax :: a }
251+type Seen = Map (CI Text) {- Prefix -} (Set (CI Text)) {- Inhabitants -}
334−instance Pretty a => ToJSON (Syn a) where
335− toJSON = toJSON . prettyPrint . syntax
337−data Fun = Fun' Name (Maybe Help) Rendered Rendered
253+data Memo = Memo
338−
254+ { _context :: Service (Fix Schema)
339−instance ToJSON Fun where
255+ , _typed :: Map Global TType
340− toJSON (Fun' n h s d) = object
256+ , _derived :: Map Global [Derive]
341− [ "name" .= Syn n
257+ , _schemas :: Map Global (Schema Global)
342− , "help" .= h
258+ , _prefixed :: Map Global Prefix
343− , "sig" .= s
259+ , _branches :: Seen
344− , "decl" .= d
260+ , _fields :: Seen
347−data Branch = Branch Name Text Help
349−instance ToJSON Branch where
350− toJSON (Branch n v h) = object
353− , "help" .= Below 6 h
357− = Sum Name (Maybe Help) [Branch]
358− | Prod Name (Maybe Help) Rendered Fun [Fun] [Rendered]
360−instance ToJSON Data where
362− Sum n h bs -> object
369− Prod n h d c ls is -> object
379−dataName :: Data -> Name
382− Prod n _ _ _ _ _ -> n
386− , _actPrettyName :: Text
388− , _actHelp :: Maybe Help
389− , _actAlias :: Rendered
393−actionTypeName :: Action -> Name
263+initial :: Service (Fix Schema) -> Memo
394−actionTypeName = dataName . _actData
264+initial s = Memo s mempty mempty mempty mempty mempty mempty
396−instance ToJSON Action where
397− toJSON Action {..} = object
398− [ "name" .= Syn _actName
399− , "prettyName" .= _actPrettyName
401− , "help" .= _actHelp
402− , "alias" .= _actAlias
403− , "type" .= _actData
406−data API = API
266+makeLenses ''Memo
408− , _apiDecl :: Rendered
409− , _apiActions :: Map Name Action
413−instance ToJSON API where
268+instance HasService Memo (Fix Schema) where
414− toJSON API {..} = object
269+ service = context
415− [ "name" .= Syn _apiName
416− , "decl" .= _apiDecl
417− , "actions" .= sortOn _actName (Map.elems _apiActions)
421−data Method a = Method
425− , _mDescription :: Maybe Help
426− , _mParameters :: Map Local (Param a)
427− , _mParameterOrder :: [Local]
428− , _mScopes :: [Text]
429− , _mRequest :: Maybe Global
430− , _mResponse :: Maybe Global
431− } deriving (Generic, Functor, Foldable, Traversable)
433−instance FromJSON a => FromJSON (Method a) where
434− parseJSON = withObject "method" $ \o -> Method
437− <*> o .: "httpMethod"
438− <*> o .:? "description"
439− <*> o .:? "parameters" .!= mempty
440− <*> o .:? "parameterOrder" .!= mempty
441− <*> o .:? "scopes" .!= mempty
443− <*> ref o "response"
445− ref o k = Just <$> (o .: k >>= (.: "$ref"))
449− = Top (Map Local (Resource a))
450− | Sub (Map Local (Method a))
451− deriving (Functor, Foldable, Traversable)
453−instance FromJSON a => FromJSON (Resource a) where
454− parseJSON = withObject "resource" $ \o ->
455− Top <$> o .: "resources"
456− <|> Sub <$> o .: "methods"
458−data Service s p r = Service
459− { _svcLibrary :: Text
462− , _svcCanonicalName :: Text
463− , _svcDescription :: Help
464− , _svcRevision :: Maybe Text
465− , _svcVersion :: Text
466− , _svcOwnerName :: Text
467− , _svcOwnerDomain :: Text
468− , _svcPackagePath :: Maybe Text
469− , _svcDocumentationLink :: Text
470− , _svcProtocol :: Protocol
471− , _svcBaseUrl :: Text
472− , _svcRootUrl :: Text
473− , _svcServicePath :: Text
474− , _svcBatchPath :: Text
475− , _svcAuth :: Maybe Object
476− , _svcParameters :: p
479− } deriving (Generic)
481−instance (FromJSON s, FromJSON p, FromJSON r) => FromJSON (Service s p r) where
482− parseJSON = withObject "service" $ \o -> Service
486− <*> o .: "canonicalName"
487− <*> o .: "description"
488− <*> o .:? "revision"
490− <*> o .: "ownerName"
491− <*> o .: "ownerDomain"
492− <*> o .:? "packagePath"
493− <*> o .: "documentationLink"
497− <*> o .: "servicePath"
498− <*> o .: "batchPath"
500− <*> o .: "parameters"
502− <*> parseJSON (Object o)
504−instance (ToJSON s, ToJSON p) => ToJSON (Service s p API) where
505− toJSON s = Object (x <> y)
507− Object x = genericToJSON (js "_svc") s
509− [ "exposedModules" .= exposedModules s
512−type Parsed = Service (Map Id (Fix Schema))
271+type AST = ExceptT Error (State Memo)
513− (Map Local (Param (Fix Schema)))
514− (Resource (Fix Schema))
516−type Flattened = Service [Id] [Param Id] (Resource Id)
517−type Typed = Service [Solved] [Param Solved] (Resource Solved)
518−type Printed = Service [Data] () API
520−svcAbbrev, svcURL :: Service s p r -> Text
521−svcAbbrev = upperHead . renameAbbrev . _svcCanonicalName
522−svcURL = (<> "URL") . lowerHead . svcAbbrev
524−svcActions :: Service s p API -> [Action]
525−svcActions = Map.elems . _apiActions . _svcApi
527−tocImports, typeImports, prodImports, sumImports, actionImports
528− :: Service s p r -> [NS]
529−tocImports _ = ["Network.Google.Prelude"]
530−typeImports s = sort ["Network.Google.Prelude", prodNS s, sumNS s]
531−prodImports s = sort ["Network.Google.Prelude", sumNS s]
532−sumImports _ = sort ["Network.Google.Prelude"]
533−actionImports s = sort ["Network.Google.Prelude", typesNS s]
535−tocNS, typesNS, prodNS, sumNS :: Service s p r -> NS
536−tocNS s = NS $ "Network" : "Google" : Text.split (== '.') (_svcCanonicalName s)
537−typesNS = (<> "Types") . tocNS
538−prodNS = (<> "Product") . typesNS
539−sumNS = (<> "Sum") . typesNS
541−actionNS :: [Text] -> NS
542−actionNS = mappend (NS ["Network", "Google", "API"]) . NS
544−exposedModules :: Service s p API -> [NS]
545−exposedModules s = sort (tocNS s : typesNS s : map _actNS (svcActions s))
547−otherModules :: Service s p r -> [NS]
548−otherModules s = sort [prodNS s, sumNS s]
550−data Library = Library
553− , _libVersions :: Versions
554− , _libServices :: NonEmpty Printed
557−instance ToJSON Library where
558− toJSON Library{..} = object
559− [ "libraryName" .= _libName
560− , "libraryTitle" .= _libTitle
561− , "libraryVersion" .= _libraryVersion _libVersions
562− , "libraryDescriptions" .= map (Desc 4 . _svcDescription) (NE.toList _libServices)
563− , "coreVersion" .= _coreVersion _libVersions
564− , "clientVersion" .= _clientVersion _libVersions
565− , "exposedModules" .= concatMap exposedModules (NE.toList _libServices)
566− , "otherModules" .= concatMap otherModules (NE.toList _libServices)
569−merge :: Versions -> [Printed] -> [Library]
570−merge v = map go . groupBy (on (==) _svcLibrary)
573− go (x:xs) = (mk x) { _libServices = x :| xs }
575− go [] = error "Empty merge set!"
578− { _libName = renameLibrary (_svcLibrary x)
579− , _libTitle = renameTitle (_svcTitle x)
581− , _libServices = x :| []