@@ -1,27 +1,25 @@
11{-# OPTIONS_GHC -Wall #-}
22{-# LANGUAGE OverloadedStrings #-}
3−module Elm.Project.Json
4− ( Project(..)
5− , AppInfo(..)
6− , PkgInfo(..)
3+module Elm.Outline
4+ ( Outline(..)
5+ , AppOutline(..)
6+ , PkgOutline(..)
77 , Exposed(..)
8− , defaultSummary
9− -- json
8+ , read
109 , write
1110 , encode
12− , read
13− , pkgDecoder
14− -- queries
11+ , decoder
12+ , defaultSummary
1513 , isPlatformPackage
1614 , getName
1715 , getExposed
18− , check
1916 )
2017 where
2118
2219
2320import Prelude hiding (read)
24−import Control.Monad.Trans (liftIO)
21+import Control.Monad (filterM, liftM)
22+import Data.Binary (Binary, get, put, getWord8, putWord8)
2523import Data.Foldable (traverse_)
2624import qualified Data.Map as Map
2725import qualified Data.Utf8 as Utf8
@@ -37,39 +35,30 @@ import qualified Json.Decode as D
3735import qualified Json.Encode as E
3836import qualified Reporting.Exit as Exit
3937import qualified Reporting.Exit.Assets as E
40−import qualified Reporting.Task as Task
41−
42−
43−
44−-- PROJECT
4538
4639
47−data Project
48− = App AppInfo
49− | Pkg PkgInfo
5040
41+-- OUTLINE
5142
5243
53−-- APPLICATION
44+data Outline
45+ = App AppOutline
46+ | Pkg PkgOutline
5447
5548
56−data AppInfo =
57− AppInfo
49+data AppOutline =
50+ AppOutline
5851 { _app_elm_version :: V.Version
5952 , _app_source_dirs :: [FilePath]
6053 , _app_deps_direct :: Map.Map Pkg.Name V.Version
61− , _app_deps_trans :: Map.Map Pkg.Name V.Version
54+ , _app_deps_indirect :: Map.Map Pkg.Name V.Version
6255 , _app_test_direct :: Map.Map Pkg.Name V.Version
63− , _app_test_trans :: Map.Map Pkg.Name V.Version
56+ , _app_test_indirect :: Map.Map Pkg.Name V.Version
6457 }
6558
6659
67−
68−-- PACKAGE
69−
70−
71−data PkgInfo =
72− PkgInfo
60+data PkgOutline =
61+ PkgOutline
7362 { _pkg_name :: Pkg.Name
7463 , _pkg_summary :: Utf8.String
7564 , _pkg_license :: Licenses.License
@@ -99,76 +88,54 @@ defaultSummary =
9988-- QUERIES
10089
10190
102−isPlatformPackage :: Project -> Bool
103−isPlatformPackage project =
104− case project of
91+isPlatformPackage :: Outline -> Bool
92+isPlatformPackage outline =
93+ case outline of
10594 App _ ->
10695 False
10796
108− Pkg info ->
109− let
110− (Pkg.Name author _) =
111− _pkg_name info
112− in
97+ Pkg pkgOutline ->
98+ let author = Pkg._author (_pkg_name pkgOutline) in
11399 author == "elm" || author == "elm-explorations"
114100
115101
116−getName :: Project -> Pkg.Name
117−getName project =
118− case project of
102+getName :: Outline -> Pkg.Name
103+getName outline =
104+ case outline of
119105 App _ ->
120106 Pkg.dummyName
121107
122− Pkg info ->
123− _pkg_name info
124−
125−
126−getExposed :: PkgInfo -> [ModuleName.Raw]
127−getExposed info =
128− case _pkg_exposed info of
129− ExposedList modules ->
130− modules
131−
132− ExposedDict chunks ->
133− concatMap snd chunks
134−
135−
136−check :: Project -> Task.Task ()
137−check project =
138− case project of
139− Pkg (PkgInfo name _ _ _ _ deps _ _) ->
140− if name == Pkg.core || Map.member Pkg.core deps
141− then return ()
142− else throwBadJson E.NoPkgCore
108+ Pkg (PkgOutline name _ _ _ _ _ _ _) ->
109+ name
143110
144− App (AppInfo _ _ direct indirect _ _) ->
145− if Map.member Pkg.core direct then
146111
147− if Map.member Pkg.json direct || Map.member Pkg.json indirect
148− then return ()
149− else throwBadJson E.NoAppJson
112+getExposed :: PkgOutline -> [ModuleName.Raw]
113+getExposed (PkgOutline _ _ _ _ exposed _ _ _) =
114+ case exposed of
115+ ExposedList names ->
116+ names
150117
151− else
152− throwBadJson E.NoAppCore
118+ ExposedDict sections ->
119+ concatMap snd sections
153120
154121
155122
156123-- WRITE
157124
158125
159−write :: FilePath -> Project -> IO ()
160−write root project =
161− E.write (root </> "elm.json") (encode project)
126+write :: FilePath -> Outline -> IO ()
127+write root outline =
128+ E.write (root </> "elm.json") (encode outline)
162129
163130
164131
165132-- JSON ENCODE
166133
167134
168−encode :: Project -> E.Value
169−encode project =
170− case project of
171− App (AppInfo elm srcDirs depsDirect depsTrans testDirect testTrans) ->
135+encode :: Outline -> E.Value
136+encode outline =
137+ case outline of
138+ App (AppOutline elm srcDirs depsDirect depsTrans testDirect testTrans) ->
172139 E.object
173140 [ "type" ==> E.string "application"
174141 , "source-directories" ==> E.list (E.string . Utf8.fromChars) srcDirs
@@ -185,10 +152,10 @@ encode project =
185152 ]
186153 ]
187154
188− Pkg (PkgInfo name summary license version exposed deps tests elm) ->
155+ Pkg (PkgOutline name summary license version exposed deps tests elm) ->
189156 E.object
190157 [ "type" ==> E.string "package"
191− , "name" ==> E.string (Pkg.toString name)
158+ , "name" ==> Pkg.encode name
192159 , "summary" ==> E.string summary
193160 , "license" ==> Licenses.encode license
194161 , "version" ==> V.encode version
@@ -228,34 +195,28 @@ encodeDeps encodeValue deps =
228195-- PARSE AND VERIFY
229196
230197
231−read :: FilePath -> Task.Task Project
232−read path =
233− do result <- liftIO $ D.fromFile decoder path
198+read :: FilePath -> IO (Either Exit.OutlineProblem Outline)
199+read root =
200+ do result <- D.fromFile decoder (root </> "elm.json")
234201 case result of
235− Right project ->
236− case project of
237− Pkg _ ->
238− do return project
239−
240− App (AppInfo _ srcDirs _ _ _ _) ->
241− do mapM_ doesDirectoryExist srcDirs
242− return project
243−
244202 Left err ->
245− throwBadJson (error "TODO bad json" err)
203+ return $ Left (Exit.BadOutlineStructure err)
246204
205+ Right outline ->
206+ case outline of
207+ Pkg _ ->
208+ return $ Right outline
247209
248−throwBadJson :: E.ElmJsonProblem -> Task.Task a
249−throwBadJson problem =
250− Task.throw (Exit.Assets (E.BadElmJson problem))
210+ App (AppOutline _ srcDirs _ _ _ _) ->
211+ do badDirs <- filterM (isBadSrcDir root) srcDirs
212+ case badDirs of
213+ [] -> return $ Right outline
214+ d:ds -> return $ Left (Exit.BadOutlineSrcDirs d ds)
251215
252216
253−doesDirectoryExist :: FilePath -> Task.Task ()
254−doesDirectoryExist dir =
255− do exists <- liftIO $ Dir.doesDirectoryExist dir
256− if exists
257− then return ()
258− else throwBadJson (E.BadSrcDir dir)
217+isBadSrcDir :: FilePath -> FilePath -> IO Bool
218+isBadSrcDir root dir =
219+ not <$> Dir.doesDirectoryExist (root </> dir)
259220
260221
261222
@@ -266,7 +227,7 @@ type Decoder a =
266227 D.Decoder E.BadElmJsonContent a
267228
268229
269−decoder :: Decoder Project
230+decoder :: Decoder Outline
270231decoder =
271232 do tipe <- D.field "type" D.string
272233 case tipe of
@@ -280,9 +241,9 @@ decoder =
280241 D.failure (E.BadType other)
281242
282243
283−appDecoder :: Decoder AppInfo
244+appDecoder :: Decoder AppOutline
284245appDecoder =
285− AppInfo
246+ AppOutline
286247 <$> D.field "elm-version" versionDecoder
287248 <*> D.field "source-directories" (D.list dirDecoder)
288249 <*> D.field "dependencies" (D.field "direct" (depsDecoder versionDecoder))
@@ -291,12 +252,12 @@ appDecoder =
291252 <*> D.field "test-dependencies" (D.field "indirect" (depsDecoder versionDecoder))
292253
293254
294−pkgDecoder :: Decoder PkgInfo
255+pkgDecoder :: Decoder PkgOutline
295256pkgDecoder =
296− PkgInfo
297− <$> D.field "name" pkgNameDecoder
257+ PkgOutline
258+ <$> D.field "name" (D.mapError E.BadPkgName Pkg.decoder)
298259 <*> D.field "summary" summaryDecoder
299− <*> D.field "license" licenseDecoder
260+ <*> D.field "license" (Licenses.decoder E.BadLicense)
300261 <*> D.field "version" versionDecoder
301262 <*> D.field "exposed-modules" exposedDecoder
302263 <*> D.field "dependencies" (depsDecoder constraintDecoder)
@@ -308,11 +269,6 @@ pkgDecoder =
308269-- JSON DECODE HELPERS
309270
310271
311−pkgNameDecoder :: Decoder Pkg.Name
312−pkgNameDecoder =
313− D.mapError E.BadPkgName Pkg.decoder
314−
315−
316272summaryDecoder :: Decoder Utf8.String
317273summaryDecoder =
318274 do summary <- D.string
@@ -321,11 +277,6 @@ summaryDecoder =
321277 else D.failure E.BadSummaryTooLong
322278
323279
324−licenseDecoder :: Decoder Licenses.License
325−licenseDecoder =
326− Licenses.decoder E.BadLicense
327−
328−
329280versionDecoder :: Decoder V.Version
330281versionDecoder =
331282 D.mapError E.BadVersion V.decoder
@@ -379,7 +330,68 @@ moduleDecoder =
379330
380331checkHeader :: Utf8.String -> Decoder ()
381332checkHeader header =
382− if Utf8.size header < 20 then
383− return ()
384− else
385− D.failure (E.BadModuleHeaderTooLong header)
333+ if Utf8.size header < 20
334+ then return ()
335+ else D.failure (E.BadModuleHeaderTooLong header)
336+
337+
338+
339+-- BINARY
340+
341+
342+instance Binary Outline where
343+ put outline =
344+ case outline of
345+ App a -> putWord8 0 >> put a
346+ Pkg a -> putWord8 1 >> put a
347+
348+ get =
349+ do n <- getWord8
350+ case n of
351+ 0 -> liftM App get
352+ 1 -> liftM Pkg get
353+ _ -> error "binary encoding of Outline was corrupted"
354+
355+
356+instance Binary AppOutline where
357+ put (AppOutline a b c d e f) =
358+ put a >> put b >> put c >> put d >> put e >> put f
359+
360+ get =
361+ do a <- get
362+ b <- get
363+ c <- get
364+ d <- get
365+ e <- get
366+ f <- get
367+ return (AppOutline a b c d e f)
368+
369+
370+instance Binary PkgOutline where
371+ put (PkgOutline a b c d e f g h) =
372+ put a >> put b >> put c >> put d >> put e >> put f >> put g >> put h
373+
374+ get =
375+ do a <- get
376+ b <- get
377+ c <- get
378+ d <- get
379+ e <- get
380+ f <- get
381+ g <- get
382+ h <- get
383+ return (PkgOutline a b c d e f g h)
384+
385+
386+instance Binary Exposed where
387+ put exposed =
388+ case exposed of
389+ ExposedList a -> putWord8 0 >> put a
390+ ExposedDict a -> putWord8 1 >> put a
391+
392+ get =
393+ do n <- getWord8
394+ case n of
395+ 0 -> liftM ExposedList get
396+ 1 -> liftM ExposedDict get
397+ _ -> error "binary encoding of ProjectDeps was corrupted"