@@ -1,3 +1,6 @@
1+{-# LANGUAGE RecordWildCards #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TupleSections #-}
14
25module Language.PureScript.Docs.Types
36 ( module Language.PureScript.Docs.Types
@@ -5,24 +8,47 @@ module Language.PureScript.Docs.Types
58 )
69 where
710
11+import Control.Arrow (first, (***))
12+import Control.Applicative ((<$>), (<*>))
13+import Data.Char
14+import Data.Maybe (mapMaybe)
815import Data.Version
16+import Data.Aeson ((.=))
17+import qualified Data.Aeson as A
18+import Data.Aeson.BetterErrors
19+import Text.ParserCombinators.ReadP (readP_to_S)
20+import Data.Text (Text)
21+import qualified Data.Text as T
22+
23+import Web.Bower.PackageMeta hiding (Version)
24+
925import qualified Language.PureScript as P
1026
11−import Language.PureScript.Docs.RenderedCode as ReExports (RenderedCode, ContainingModule(..))
27+import Language.PureScript.Docs.RenderedCode as ReExports
28+ (RenderedCode, asRenderedCode,
29+ ContainingModule(..), asContainingModule,
30+ RenderedCodeElement(..), asRenderedCodeElement)
1231
13−data RenderedPackage = RenderedPackage
14− { rpName :: String
15− , rpVersion :: Version
16− , rpModules :: [RenderedModule]
32+--------------------
33+-- Types
34+
35+data UploadedPackage = UploadedPackage
36+ { pkgMeta :: PackageMeta
37+ , pkgVersion :: Version
38+ , pkgModules :: [RenderedModule]
39+ , pkgBookmarks :: [Bookmark]
40+ , pkgResolvedDependencies :: [(PackageName, Version)]
41+ , pkgGithub :: (GithubUser, GithubRepo)
42+ , pkgUploader :: Maybe GithubUser
1743 }
18− deriving (Show)
44+ deriving (Show, Eq, Ord)
1945
2046data RenderedModule = RenderedModule
2147 { rmName :: String
2248 , rmComments :: Maybe String
2349 , rmDeclarations :: [RenderedDeclaration]
2450 }
25− deriving (Show)
51+ deriving (Show, Eq, Ord)
2652
2753data RenderedDeclaration = RenderedDeclaration
2854 { rdTitle :: String
@@ -31,7 +57,7 @@ data RenderedDeclaration = RenderedDeclaration
3157 , rdSourceSpan :: Maybe P.SourceSpan
3258 , rdChildren :: [RenderedChildDeclaration]
3359 }
34− deriving (Show)
60+ deriving (Show, Eq, Ord)
3561
3662data RenderedChildDeclaration = RenderedChildDeclaration
3763 { rcdTitle :: String
@@ -40,10 +66,213 @@ data RenderedChildDeclaration = RenderedChildDeclaration
4066 , rcdSourceSpan :: Maybe P.SourceSpan
4167 , rcdType :: RenderedChildDeclarationType
4268 }
43− deriving (Show)
69+ deriving (Show, Eq, Ord)
4470
4571data RenderedChildDeclarationType
4672 = ChildInstance
4773 | ChildDataConstructor
4874 | ChildTypeClassMember
75+ deriving (Show, Eq, Ord, Bounded, Enum)
76+
77+childDeclTypeToString :: RenderedChildDeclarationType -> String
78+childDeclTypeToString = withHead toLower . drop 5 . show
79+ where
80+ withHead f (x:xs) = f x : xs
81+ withHead _ [] = []
82+
83+childDeclarationTypes :: [(String, RenderedChildDeclarationType)]
84+childDeclarationTypes =
85+ map (\t -> (childDeclTypeToString t, t)) [minBound .. maxBound]
86+
87+newtype GithubUser
88+ = GithubUser { runGithubUser :: String }
89+ deriving (Show, Eq, Ord)
90+
91+newtype GithubRepo
92+ = GithubRepo { runGithubRepo :: String }
4993 deriving (Show, Eq, Ord)
94+
95+data UploadedPackageError
96+ = ErrorInPackageMeta BowerError
97+ | InvalidVersion
98+ | InvalidDeclarationType
99+ | InvalidRenderedCode String
100+ deriving (Show, Eq, Ord)
101+
102+type Bookmark = InPackage (P.ModuleName, String)
103+
104+data InPackage a
105+ = Local a
106+ | FromDep PackageName a
107+ deriving (Show, Eq, Ord)
108+
109+instance Functor InPackage where
110+ fmap f (Local x) = Local (f x)
111+ fmap f (FromDep pkgName x) = FromDep pkgName (f x)
112+
113+takeLocal :: InPackage a -> Maybe a
114+takeLocal (Local a) = Just a
115+takeLocal _ = Nothing
116+
117+takeLocals :: [InPackage a] -> [a]
118+takeLocals = mapMaybe takeLocal
119+
120+ignorePackage :: InPackage a -> a
121+ignorePackage (Local x) = x
122+ignorePackage (FromDep _ x) = x
123+
124+----------------------
125+-- Parsing
126+
127+asUploadedPackage :: Parse UploadedPackageError UploadedPackage
128+asUploadedPackage =
129+ UploadedPackage <$> key "packageMeta" asPackageMeta .! ErrorInPackageMeta
130+ <*> key "version" asVersion
131+ <*> key "modules" (eachInArray asRenderedModule)
132+ <*> key "bookmarks" asBookmarks .! ErrorInPackageMeta
133+ <*> key "resolvedDependencies" asResolvedDependencies
134+ <*> key "github" asGithub
135+ <*> keyMay "uploader" (GithubUser <$> asString)
136+
137+asVersion :: Parse UploadedPackageError Version
138+asVersion = withString (maybe (Left InvalidVersion) Right . parseVersion')
139+
140+parseVersion' :: String -> Maybe Version
141+parseVersion' str =
142+ case filter (null . snd) $ readP_to_S parseVersion str of
143+ [(vers, "")] -> Just vers
144+ _ -> Nothing
145+
146+asRenderedModule :: Parse UploadedPackageError RenderedModule
147+asRenderedModule =
148+ RenderedModule <$> key "name" asString
149+ <*> key "comments" (perhaps asString)
150+ <*> key "declarations" (eachInArray asDeclaration)
151+
152+asDeclaration :: Parse UploadedPackageError RenderedDeclaration
153+asDeclaration =
154+ RenderedDeclaration <$> key "title" asString
155+ <*> key "comments" (perhaps asString)
156+ <*> key "code" asRenderedCode .! InvalidRenderedCode
157+ <*> key "sourceSpan" (perhaps asSourceSpan)
158+ <*> key "children" (eachInArray asRenderedChildDeclaration)
159+
160+asRenderedChildDeclaration :: Parse UploadedPackageError RenderedChildDeclaration
161+asRenderedChildDeclaration =
162+ RenderedChildDeclaration <$> key "title" asString
163+ <*> key "comments" (perhaps asString)
164+ <*> key "code" asRenderedCode .! InvalidRenderedCode
165+ <*> key "sourceSpan" (perhaps asSourceSpan)
166+ <*> key "type" asRenderedChildDeclarationType .!! InvalidDeclarationType
167+ where
168+ p .!! err = p .! const err
169+
170+asRenderedChildDeclarationType :: Parse UploadedPackageError RenderedChildDeclarationType
171+asRenderedChildDeclarationType =
172+ withString (maybe (Left InvalidDeclarationType) Right .
173+ flip lookup childDeclarationTypes)
174+
175+asSourcePos :: Parse e P.SourcePos
176+asSourcePos = P.SourcePos <$> nth 0 asIntegral
177+ <*> nth 1 asIntegral
178+
179+asBookmarks :: Parse BowerError [Bookmark]
180+asBookmarks = eachInArray asBookmark
181+
182+asBookmark :: Parse BowerError Bookmark
183+asBookmark =
184+ build <$> key "package" (perhaps (withString parsePackageName))
185+ <*> key "item" ((,) <$> nth 0 (P.moduleNameFromString <$> asString)
186+ <*> nth 1 asString)
187+ where
188+ build Nothing = Local
189+ build (Just pn) = FromDep pn
190+
191+asResolvedDependencies :: Parse UploadedPackageError [(PackageName, Version)]
192+asResolvedDependencies =
193+ eachInObjectWithKey (mapLeft ErrorInPackageMeta . parsePackageName . T.unpack) asVersion
194+ where
195+ mapLeft f (Left x) = Left (f x)
196+ mapLeft _ (Right x) = Right x
197+
198+asGithub :: Parse e (GithubUser, GithubRepo)
199+asGithub = (,) <$> nth 0 (GithubUser <$> asString)
200+ <*> nth 1 (GithubRepo <$> asString)
201+
202+asSourceSpan :: Parse e P.SourceSpan
203+asSourceSpan = P.SourceSpan <$> key "name" asString
204+ <*> key "start" asSourcePos
205+ <*> key "end" asSourcePos
206+
207+---------------------
208+-- ToJSON instances
209+
210+instance A.ToJSON UploadedPackage where
211+ toJSON UploadedPackage{..} =
212+ A.object $
213+ [ "packageMeta" .= pkgMeta
214+ , "version" .= showVersion pkgVersion
215+ , "modules" .= pkgModules
216+ , "bookmarks" .= map (fmap (first P.runModuleName)) pkgBookmarks
217+ , "resolvedDependencies" .= assocListToJSON (T.pack . runPackageName)
218+ (T.pack . showVersion)
219+ pkgResolvedDependencies
220+ , "github" .= pkgGithub
221+ ] ++ case pkgUploader of
222+ Just u -> [ "uploader" .= u ]
223+ Nothing -> []
224+
225+instance A.ToJSON RenderedModule where
226+ toJSON RenderedModule{..} =
227+ A.object [ "name" .= rmName
228+ , "comments" .= rmComments
229+ , "declarations" .= rmDeclarations
230+ ]
231+
232+instance A.ToJSON RenderedDeclaration where
233+ toJSON RenderedDeclaration{..} =
234+ A.object [ "title" .= rdTitle
235+ , "comments" .= rdComments
236+ , "code" .= rdCode
237+ , "sourceSpan" .= rdSourceSpan
238+ , "children" .= rdChildren
239+ ]
240+
241+instance A.ToJSON RenderedChildDeclaration where
242+ toJSON RenderedChildDeclaration{..} =
243+ A.object [ "title" .= rcdTitle
244+ , "comments" .= rcdComments
245+ , "code" .= rcdCode
246+ , "sourceSpan" .= rcdSourceSpan
247+ , "type" .= rcdType
248+ ]
249+
250+instance A.ToJSON RenderedChildDeclarationType where
251+ toJSON = A.toJSON . childDeclTypeToString
252+
253+instance A.ToJSON GithubUser where
254+ toJSON = A.toJSON . runGithubUser
255+
256+instance A.ToJSON GithubRepo where
257+ toJSON = A.toJSON . runGithubRepo
258+
259+-- | Given a function for turning association list keys into JSON object keys,
260+-- and a function for turning association list values to JSON string values,
261+-- turns an association list into a JSON object.
262+--
263+-- For example:
264+-- @assocListToJSON T.pack T.pack [("a", "b")]@ will give @{"a": "b"}@.
265+assocListToJSON :: (a -> Text) -> (b -> Text) -> [(a, b)] -> A.Value
266+assocListToJSON f g xs = A.object (map (uncurry (.=) . (f *** g)) xs)
267+
268+instance A.ToJSON a => A.ToJSON (InPackage a) where
269+ toJSON x =
270+ case x of
271+ Local y -> withPackage (Nothing :: Maybe ()) y
272+ FromDep pn y -> withPackage (Just pn) y
273+ where
274+ withPackage :: (A.ToJSON p, A.ToJSON x) => p -> x -> A.Value
275+ withPackage p y =
276+ A.object [ "package" .= p
277+ , "item" .= y
278+ ]