@@ -6,15 +6,16 @@
66{-# LANGUAGE ExtendedDefaultRules #-}
77{-# LANGUAGE FlexibleContexts #-}
88{-# LANGUAGE FlexibleInstances #-}
9+{-# LANGUAGE FunctionalDependencies #-}
910{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1011{-# LANGUAGE KindSignatures #-}
1112{-# LANGUAGE LambdaCase #-}
13+{-# LANGUAGE MultiParamTypeClasses #-}
1214{-# LANGUAGE OverloadedStrings #-}
15+{-# LANGUAGE RankNTypes #-}
1316{-# LANGUAGE RecordWildCards #-}
1417{-# LANGUAGE TemplateHaskell #-}
1518
16−{-# OPTIONS_GHC -fno-warn-type-defaults #-}
17−
1819-- Module : Gen.Types
1920-- Copyright : (c) 2015 Brendan Hay
2021-- License : Mozilla Public License, v. 2.0.
@@ -28,12 +29,19 @@ module Gen.Types
2829 , module Gen.Types.Id
2930 , module Gen.Types.Map
3031 , module Gen.Types.NS
32+ , module Gen.Types.Schema
33+ , module Gen.Types.Data
3134 ) where
3235
3336import Control.Applicative
3437import Control.Lens hiding ((.=))
35−import Data.Aeson hiding (Bool, String)
38+import Control.Monad.Except
39+import Control.Monad.State.Strict
40+import Data.Aeson hiding (Array, Bool, String)
41+import qualified Data.Aeson as A
3642import Data.Aeson.TH
43+import Data.CaseInsensitive (CI)
44+import qualified Data.CaseInsensitive as CI
3745import Data.Char
3846import Data.Function (on)
3947import qualified Data.HashMap.Strict as Map
@@ -54,37 +62,23 @@ import qualified Filesystem.Path.CurrentOS as Path
5462import Formatting
5563import Gen.Orphans ()
5664import Gen.Text
65+import Gen.TH
66+import Gen.Types.Data
5767import Gen.Types.Help
5868import Gen.Types.Id
5969import Gen.Types.Map
6070import Gen.Types.NS
71+import Gen.Types.Schema
6172import GHC.Generics
6273import GHC.TypeLits
6374import Language.Haskell.Exts (Name)
6475import Language.Haskell.Exts.Pretty (Pretty, prettyPrint)
76+import Prelude hiding (Enum)
6577import Text.EDE (Template)
6678
67−default (Text)
68−
69−newtype Fix f = Fix (f (Fix f))
70−
71−type Set = Set.HashSet
72−type Error = LText.Text
73−type Rendered = LText.Text
74−type Path = Path.FilePath
75−
76−toTextIgnore :: Path -> Text
77−toTextIgnore = either id id . Path.toText
78−
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
87− }
79+type Set = Set.HashSet
80+type Error = LText.Text
81+type Path = Path.FilePath
8882
8983newtype Version (v :: Symbol) = Version Text
9084 deriving (Eq, Show)
@@ -105,193 +99,118 @@ data Versions = Versions
10599 , _coreVersion :: CoreVer
106100 } deriving (Show)
107101
108−-- FIXME: need a more comprehensive 'vm_alpha' vs 'vm1.1' etc check.
109−data Spec = Spec
110− { _specName :: Text
111− , _specPrefix :: Text
112− , _specVersion :: Text
113− , _specPath :: Path
102+makeClassy ''Versions
103+
104+-- FIXME: need a more comprehensive 'vm_alpha' vs 'vm1.1' version check.
105+data Model = Model
106+ { modelName :: Text
107+ , modelPrefix :: Text
108+ , modelVersion :: Text
109+ , modelPath :: Path
114110 }
115111
116−instance Eq Spec where
117− (==) = on (==) _specPrefix
112+instance Eq Model where
113+ (==) = on (==) modelPrefix
118114
119−instance Ord Spec where
115+instance Ord Model where
120116 compare a b =
121− on compare _specPrefix a b
122− <> on compare (Down . _specVersion) a b
117+ on compare modelPrefix a b
118+ <> on compare (Down . modelVersion) a b
123119
124−specFromPath :: Path -> Spec
125−specFromPath x = Spec n p v x
120+modelFromPath :: Path -> Model
121+modelFromPath x = Model n p v x
126122 where
127− -- FIXME:
128− n = Text.init
129− . Text.intercalate "/"
130− . drop 1
131− . dropWhile (/= "model")
132− $ Text.split (== '/') p
123+ n = Text.init
124+ . Text.intercalate "/"
125+ . drop 1
126+ . dropWhile (/= "model")
127+ $ Text.split (== '/') p
133128
134− p = toTextIgnore (Path.parent (Path.parent x))
135− v = toTextIgnore (Path.dirname x)
129+ p = toTextIgnore (Path.parent (Path.parent x))
130+ v = toTextIgnore (Path.dirname x)
136131
137−data Protocol = REST
138− deriving (Eq, Show)
132+data Templates = Templates
133+ { cabalTemplate :: Template
134+ , tocTemplate :: Template
135+ , readmeTemplate :: Template
136+ , typesTemplate :: Template
137+ , prodTemplate :: Template
138+ , sumTemplate :: Template
139+ , actionTemplate :: Template
140+ }
139141
140−instance FromJSON Protocol where
141− parseJSON = withText "protocol" $ \case
142− "rest" -> pure REST
143− e -> fail $ "Unable to parse protocol from " ++ Text.unpack e
142+tocImports, typeImports, prodImports, sumImports, actionImports
143+ :: Service a -> [NS]
144+tocImports _ = [preludeNS]
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]
149+
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
144155
145−instance ToJSON Protocol where
146− toJSON = toJSON . map toLower . show
156+preludeNS :: NS
157+preludeNS = "Network.Google.Prelude"
147158
148−data Location
149− = Query
150− | Path
151− deriving (Eq, Show)
159+-- actionNS :: [Text] -> NS
160+-- actionNS = mappend (NS ["Network", "Google", "API"]) . NS
152161
153−deriveJSON (js "") ''Location
154−
155−data Info = Info
156− { _description :: Maybe Help
157− , _defaulted :: Maybe Text
158− , _required :: !Bool
159− , _location :: Maybe Location
160− } deriving (Eq, Show)
161−
162−instance FromJSON Info where
163− parseJSON = withObject "info" $ \o -> Info
164− <$> o .:? "description"
165− <*> o .:? "default"
166− <*> o .:? "required" .!= False
167− <*> o .:? "location"
168−
169−deriveToJSON (js "_info") ''Info
170−
171−makeClassy ''Info
172−
173−parameter :: HasInfo a => a -> Bool
174−parameter s = s ^. required && isNothing (s ^. defaulted)
175−
176−data Lit
177− = Text
178− | Bool
179− | Float
180− | Double
181− | Byte
182− | UInt32
183− | UInt64
184− | Int32
185− | Int64
186− | Nat
187− | Date
188− | Time
189− deriving (Eq, Show)
162+-- exposedModules :: Service s p API -> [NS]
163+-- exposedModules s = sort (tocNS s : typesNS s : map _actNS (svcActions s))
190164
191−instance FromJSON Lit where
192− parseJSON = withObject "literal" $ \o -> do
193− n <- num =<< (o .:? "minimum" .!= "0")
194− (o .: "format" <|> o .: "type") >>= \case
195− -- Types
196− "string" -> pure Text
197− "boolean" -> pure Bool
198− -- Formats
199− "float" -> pure Float
200− "double" -> pure Double
201− "byte" -> pure (nat n Byte)
202− "uint32" -> pure (nat n UInt32)
203− "uint64" -> pure (nat n UInt64)
204− "int32" -> pure (nat n Int32)
205− "int64" -> pure (nat n Int64)
206− "date" -> pure Date
207− "date-time" -> pure Time
208− e -> fail $
209− "Unable to parse Lit from " ++ Text.unpack e
210− where
211− num = either fail (pure . fst) . Read.decimal
212−
213− nat :: Int -> Lit -> Lit
214− nat n | n > 0 = const Nat
215− | otherwise = id
216−
217−-- "pattern": "[A-Z]{3}",
218−-- "pattern": "[0-9a-zA-Z,]+",
219−-- "pattern": "[a-zA-Z]+==[a-zA-Z0-9_+-]+",
220−-- "minimum": "1"
221−-- "format": "date-time", "uint64", "int32",
222−
223−deriveToJSON (js "") ''Lit
224−
225−data Schema a
226− = Obj Info (Map Local a)
227− | Arr Info a
228− | Enum Info [Text] [Help]
229− | Lit Info Lit
230− | Ref Info Global
231− | Any Info -- String or Number
232− deriving (Eq, Show, Functor, Foldable, Traversable)
233−
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)
238− where
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)
165+-- otherModules :: Service s p r -> [NS]
166+-- otherModules s = sort [prodNS s, sumNS s]
167+
168+toTextIgnore :: Path -> Text
169+toTextIgnore = either id id . Path.toText
170+
171+data Library = Library
172+ { _lVersions :: Versions
173+ , _lService :: Service Global
174+ -- , _lMethods ::
175+ -- , _lResources :: []
176+ -- , _lSchemas :: [Data]
177+ }
244178
245− enm i o = Enum i <$> o .: "enum" <*> o .: "enumDescriptions"
246− ref i o = Ref i <$> o .: "$ref"
179+makeLenses ''Library
247180
248−deriveToJSON (js "") ''Schema
181+instance HasVersions Library where
182+ versions = lVersions
183+
184+instance HasDescription Library Global where
185+ description = lService . description
186+
187+instance HasService Library Global where
188+ service = lService
189+
190+instance ToJSON Library where
191+ toJSON l = object
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)
200+ ]
201+
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)
249210
250−instance HasInfo (Schema a) where
251− info = lens f (flip g)
252− where
253− f = \case
254− Obj i _ -> i
255− Arr i _ -> i
256− Enum i _ _ -> i
257− Lit i _ -> i
258− Ref i _ -> i
259− Any i -> i
260−
261− g i = \case
262− Obj _ p -> Obj i p
263− Arr _ e -> Arr i e
264− Enum _ v d -> Enum i v d
265− Lit _ l -> Lit i l
266− Ref _ r -> Ref i r
267− Any {} -> Any i
268−
269−data Param a = Param
270− { _prmLocation :: !Location
271− , _prmRepeated :: !Bool
272− , _prmInfo :: Info
273− , _prmSchema :: a
274− } deriving (Show, Functor, Foldable, Traversable)
275−
276−-- paramSchema :: Param -> Fix Schema
277−-- paramSchema p
278−-- | _prmRepeated p = Fix $ Arr (_prmInfo p) lit
279−-- | otherwise = lit
280−-- where
281−-- lit = Fix $ Lit (_prmInfo p) (_prmLiteral p)
282−
283−instance FromJSON a => FromJSON (Param a) where
284− parseJSON = withObject "parameter" $ \o -> Param
285− <$> o .: "location"
286− <*> o .:? "repeated" .!= False
287− <*> parseJSON (Object o)
288− <*> parseJSON (Object o)
289−
290−instance HasInfo (Param a) where
291− info = lens _prmInfo (\s a -> s { _prmInfo = a })
292211
293212data TType
294− = TType Id
213+ = TType Global
295214 | TLit Lit
296215 | TMaybe TType
297216 | TEither TType TType
@@ -314,9 +233,9 @@ data Derive
314233 deriving (Eq, Show)
315234
316235data Solved = Solved
317− { _ident :: Id
318− , _prefix :: Pre
319− , _schema :: Schema Id
236+ { _ident :: Global
237+ , _prefix :: Prefix
238+ , _schema :: Schema Global
320239 , _type :: TType
321240 , _deriving :: [Derive]
322241 }
@@ -329,254 +248,24 @@ instance HasInfo Solved where
329248monoid :: Solved -> Bool
330249monoid = elem DMonoid . _deriving
331250
332−data Syn a = Syn { syntax :: a }
333−
334−instance Pretty a => ToJSON (Syn a) where
335− toJSON = toJSON . prettyPrint . syntax
251+type Seen = Map (CI Text) {- Prefix -} (Set (CI Text)) {- Inhabitants -}
336252
337−data Fun = Fun' Name (Maybe Help) Rendered Rendered
338−
339−instance ToJSON Fun where
340− toJSON (Fun' n h s d) = object
341− [ "name" .= Syn n
342− , "help" .= h
343− , "sig" .= s
344− , "decl" .= d
345− ]
346−
347−data Branch = Branch Name Text Help
348−
349−instance ToJSON Branch where
350− toJSON (Branch n v h) = object
351− [ "name" .= Syn n
352− , "value" .= v
353− , "help" .= Below 6 h
354− ]
355−
356−data Data
357− = Sum Name (Maybe Help) [Branch]
358− | Prod Name (Maybe Help) Rendered Fun [Fun] [Rendered]
359−
360−instance ToJSON Data where
361− toJSON = \case
362− Sum n h bs -> object
363− [ "name" .= Syn n
364− , "type" .= "sum"
365− , "help" .= h
366− , "branches" .= bs
367− ]
368−
369− Prod n h d c ls is -> object
370− [ "name" .= Syn n
371− , "type" .= "prod"
372− , "decl" .= d
373− , "help" .= h
374− , "ctor" .= c
375− , "lenses" .= ls
376− , "instances" .= is
377− ]
378−
379−dataName :: Data -> Name
380−dataName = \case
381− Sum n _ _ -> n
382− Prod n _ _ _ _ _ -> n
383−
384−data Action = Action
385− { _actName :: Name
386− , _actPrettyName :: Text
387− , _actNS :: NS
388− , _actHelp :: Maybe Help
389− , _actAlias :: Rendered
390− , _actData :: Data
253+data Memo = Memo
254+ { _context :: Service (Fix Schema)
255+ , _typed :: Map Global TType
256+ , _derived :: Map Global [Derive]
257+ , _schemas :: Map Global (Schema Global)
258+ , _prefixed :: Map Global Prefix
259+ , _branches :: Seen
260+ , _fields :: Seen
391261 }
392262
393−actionTypeName :: Action -> Name
394−actionTypeName = dataName . _actData
395−
396−instance ToJSON Action where
397− toJSON Action {..} = object
398− [ "name" .= Syn _actName
399− , "prettyName" .= _actPrettyName
400− , "ns" .= _actNS
401− , "help" .= _actHelp
402− , "alias" .= _actAlias
403− , "type" .= _actData
404− ]
263+initial :: Service (Fix Schema) -> Memo
264+initial s = Memo s mempty mempty mempty mempty mempty mempty
405265
406−data API = API
407− { _apiName :: Name
408− , _apiDecl :: Rendered
409− , _apiActions :: Map Name Action
410− , _apiURL :: Fun
411− }
266+makeLenses ''Memo
412267
413−instance ToJSON API where
414− toJSON API {..} = object
415− [ "name" .= Syn _apiName
416− , "decl" .= _apiDecl
417− , "actions" .= sortOn _actName (Map.elems _apiActions)
418− , "url" .= _apiURL
419− ]
420−
421−data Method a = Method
422− { _mId :: Text
423− , _mPath :: Text
424− , _mMethod :: Text
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)
432−
433−instance FromJSON a => FromJSON (Method a) where
434− parseJSON = withObject "method" $ \o -> Method
435− <$> o .: "id"
436− <*> o .: "path"
437− <*> o .: "httpMethod"
438− <*> o .:? "description"
439− <*> o .:? "parameters" .!= mempty
440− <*> o .:? "parameterOrder" .!= mempty
441− <*> o .:? "scopes" .!= mempty
442− <*> ref o "request"
443− <*> ref o "response"
444− where
445− ref o k = Just <$> (o .: k >>= (.: "$ref"))
446− <|> pure Nothing
447−
448−data Resource a
449− = Top (Map Local (Resource a))
450− | Sub (Map Local (Method a))
451− deriving (Functor, Foldable, Traversable)
452−
453−instance FromJSON a => FromJSON (Resource a) where
454− parseJSON = withObject "resource" $ \o ->
455− Top <$> o .: "resources"
456− <|> Sub <$> o .: "methods"
457−
458−data Service s p r = Service
459− { _svcLibrary :: Text
460− , _svcTitle :: Text
461− , _svcName :: 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
477− , _svcSchemas :: s
478− , _svcApi :: r
479− } deriving (Generic)
480−
481−instance (FromJSON s, FromJSON p, FromJSON r) => FromJSON (Service s p r) where
482− parseJSON = withObject "service" $ \o -> Service
483− <$> o .: "library"
484− <*> o .: "title"
485− <*> o .: "name"
486− <*> o .: "canonicalName"
487− <*> o .: "description"
488− <*> o .:? "revision"
489− <*> o .: "version"
490− <*> o .: "ownerName"
491− <*> o .: "ownerDomain"
492− <*> o .:? "packagePath"
493− <*> o .: "documentationLink"
494− <*> o .: "protocol"
495− <*> o .: "baseUrl"
496− <*> o .: "rootUrl"
497− <*> o .: "servicePath"
498− <*> o .: "batchPath"
499− <*> o .:? "auth"
500− <*> o .: "parameters"
501− <*> o .: "schemas"
502− <*> parseJSON (Object o)
503−
504−instance (ToJSON s, ToJSON p) => ToJSON (Service s p API) where
505− toJSON s = Object (x <> y)
506− where
507− Object x = genericToJSON (js "_svc") s
508− Object y = object
509− [ "exposedModules" .= exposedModules s
510− ]
268+instance HasService Memo (Fix Schema) where
269+ service = context
511270
512−type Parsed = Service (Map Id (Fix Schema))
513− (Map Local (Param (Fix Schema)))
514− (Resource (Fix Schema))
515−
516−type Flattened = Service [Id] [Param Id] (Resource Id)
517−type Typed = Service [Solved] [Param Solved] (Resource Solved)
518−type Printed = Service [Data] () API
519−
520−svcAbbrev, svcURL :: Service s p r -> Text
521−svcAbbrev = upperHead . renameAbbrev . _svcCanonicalName
522−svcURL = (<> "URL") . lowerHead . svcAbbrev
523−
524−svcActions :: Service s p API -> [Action]
525−svcActions = Map.elems . _apiActions . _svcApi
526−
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]
534−
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
540−
541−actionNS :: [Text] -> NS
542−actionNS = mappend (NS ["Network", "Google", "API"]) . NS
543−
544−exposedModules :: Service s p API -> [NS]
545−exposedModules s = sort (tocNS s : typesNS s : map _actNS (svcActions s))
546−
547−otherModules :: Service s p r -> [NS]
548−otherModules s = sort [prodNS s, sumNS s]
549−
550−data Library = Library
551− { _libName :: Text
552− , _libTitle :: Text
553− , _libVersions :: Versions
554− , _libServices :: NonEmpty Printed
555− }
556−
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)
567− ]
568−
569−merge :: Versions -> [Printed] -> [Library]
570−merge v = map go . groupBy (on (==) _svcLibrary)
571− where
572− go [x] = mk x
573− go (x:xs) = (mk x) { _libServices = x :| xs }
574− -- FIXME:
575− go [] = error "Empty merge set!"
576−
577− mk x = Library
578− { _libName = renameLibrary (_svcLibrary x)
579− , _libTitle = renameTitle (_svcTitle x)
580− , _libVersions = v
581− , _libServices = x :| []
582− }
271+type AST = ExceptT Error (State Memo)