Skip to content

Commit 15a7069

Browse files
committed
Patch in repl API functionality for elm-notebook exploration
1 parent d8ae362 commit 15a7069

File tree

8 files changed

+483
-1
lines changed

8 files changed

+483
-1
lines changed

elm.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ Executable lamdera
309309
Test.Wire
310310
Lamdera.Evergreen.TestMigrationHarness
311311
Lamdera.Evergreen.TestMigrationGenerator
312+
Endpoint.Repl
313+
Artifacts
314+
Cors
312315

313316

314317
-- Debug helpers --
@@ -397,6 +400,7 @@ Executable lamdera
397400
-- Debug
398401
unicode-show,
399402
network-info,
403+
network-uri,
400404

401405
-- Future
402406
conduit-extra,

extra/Artifacts.hs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
{-# OPTIONS_GHC -Wall #-}
2+
module Artifacts
3+
( Artifacts(..)
4+
, loadCompile
5+
, loadRepl
6+
, toDepsInfo
7+
)
8+
where
9+
10+
11+
import Control.Concurrent (readMVar)
12+
import Control.Monad (liftM2)
13+
import qualified Data.ByteString as BS
14+
import qualified Data.ByteString.Builder as B
15+
import qualified Data.ByteString.Lazy as LBS
16+
import qualified Data.Map as Map
17+
import qualified Data.Name as N
18+
import qualified Data.OneOrMore as OneOrMore
19+
import qualified System.Directory as Dir
20+
import System.FilePath ((</>))
21+
22+
import qualified AST.Canonical as Can
23+
import qualified AST.Optimized as Opt
24+
import qualified BackgroundWriter as BW
25+
import qualified Elm.Details as Details
26+
import qualified Elm.Interface as I
27+
import qualified Elm.ModuleName as ModuleName
28+
import qualified Elm.Package as Pkg
29+
import Json.Encode ((==>))
30+
import qualified Json.Encode as E
31+
import qualified Json.String as Json
32+
import qualified Reporting
33+
34+
35+
36+
-- ARTIFACTS
37+
38+
39+
data Artifacts =
40+
Artifacts
41+
{ _ifaces :: Map.Map ModuleName.Raw I.Interface
42+
, _graph :: Opt.GlobalGraph
43+
}
44+
45+
46+
loadCompile :: IO Artifacts
47+
loadCompile =
48+
load ("outlines" </> "compile")
49+
50+
51+
loadRepl :: IO Artifacts
52+
loadRepl =
53+
load ("outlines" </> "repl")
54+
55+
56+
57+
-- LOAD
58+
59+
60+
load :: FilePath -> IO Artifacts
61+
load dir =
62+
BW.withScope $ \scope ->
63+
do putStrLn $ "Loading " ++ dir </> "elm.json"
64+
style <- Reporting.terminal
65+
root <- fmap (</> dir) Dir.getCurrentDirectory
66+
result <- Details.load style scope root
67+
case result of
68+
Left _ ->
69+
error $ "Ran into some problem loading elm.json\nTry running `lamdera make` in: " ++ dir
70+
71+
Right details ->
72+
do omvar <- Details.loadObjects root details
73+
imvar <- Details.loadInterfaces root details
74+
mdeps <- readMVar imvar
75+
mobjs <- readMVar omvar
76+
case liftM2 (,) mdeps mobjs of
77+
Nothing ->
78+
error $ "Ran into some weird problem loading elm.json\nTry running `lamdera make` in: " ++ dir
79+
80+
Just (deps, objs) ->
81+
return $ Artifacts (toInterfaces deps) objs
82+
83+
84+
toInterfaces :: Map.Map ModuleName.Canonical I.DependencyInterface -> Map.Map ModuleName.Raw I.Interface
85+
toInterfaces deps =
86+
Map.mapMaybe toUnique $ Map.fromListWith OneOrMore.more $
87+
Map.elems (Map.mapMaybeWithKey getPublic deps)
88+
89+
90+
getPublic :: ModuleName.Canonical -> I.DependencyInterface -> Maybe (ModuleName.Raw, OneOrMore.OneOrMore I.Interface)
91+
getPublic (ModuleName.Canonical _ name) dep =
92+
case dep of
93+
I.Public iface -> Just (name, OneOrMore.one iface)
94+
I.Private _ _ _ -> Nothing
95+
96+
97+
toUnique :: OneOrMore.OneOrMore a -> Maybe a
98+
toUnique oneOrMore =
99+
case oneOrMore of
100+
OneOrMore.One value -> Just value
101+
OneOrMore.More _ _ -> Nothing
102+
103+
104+
105+
-- TO DEPS INFO
106+
107+
108+
toDepsInfo :: Artifacts -> BS.ByteString
109+
toDepsInfo (Artifacts ifaces _) =
110+
LBS.toStrict $ B.toLazyByteString $ E.encodeUgly $ encode ifaces
111+
112+
113+
114+
-- ENCODE
115+
116+
117+
encode :: Map.Map ModuleName.Raw I.Interface -> E.Value
118+
encode ifaces =
119+
E.dict Json.fromName encodeInterface ifaces
120+
121+
122+
encodeInterface :: I.Interface -> E.Value
123+
encodeInterface (I.Interface pkg values unions aliases binops) =
124+
E.object
125+
[ "pkg" ==> E.chars (Pkg.toChars pkg)
126+
, "ops" ==> E.list E.name (Map.keys binops)
127+
, "values" ==> E.list E.name (Map.keys values)
128+
, "aliases" ==> E.list E.name (Map.keys (Map.filter isPublicAlias aliases))
129+
, "types" ==> E.dict Json.fromName (E.list E.name) (Map.mapMaybe toPublicUnion unions)
130+
]
131+
132+
133+
isPublicAlias :: I.Alias -> Bool
134+
isPublicAlias alias =
135+
case alias of
136+
I.PublicAlias _ -> True
137+
I.PrivateAlias _ -> False
138+
139+
140+
toPublicUnion :: I.Union -> Maybe [N.Name]
141+
toPublicUnion union =
142+
case union of
143+
I.OpenUnion (Can.Union _ variants _ _) ->
144+
Just (map getVariantName variants)
145+
146+
I.ClosedUnion _ ->
147+
Just []
148+
149+
I.PrivateUnion _ ->
150+
Nothing
151+
152+
153+
getVariantName :: Can.Ctor -> N.Name
154+
getVariantName (Can.Ctor name _ _ _) =
155+
name

extra/Cors.hs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{-# OPTIONS_GHC -Wall #-}
2+
module Cors
3+
( allow
4+
)
5+
where
6+
7+
8+
import qualified Data.HashSet as HashSet
9+
import Network.URI (parseURI)
10+
import Snap.Core (Snap, Method, method)
11+
import Snap.Util.CORS (CORSOptions(..), HashableMethod(..), OriginList(Origins), applyCORS, mkOriginSet)
12+
13+
14+
15+
-- ALLOW
16+
17+
18+
allow :: Method -> [String] -> Snap () -> Snap ()
19+
allow method_ origins snap =
20+
applyCORS (toOptions method_ origins) $ method method_ $
21+
snap
22+
23+
24+
25+
-- TO OPTIONS
26+
27+
28+
toOptions :: (Monad m) => Method -> [String] -> CORSOptions m
29+
toOptions method_ origins =
30+
let
31+
allowedOrigins = toOriginList origins
32+
allowedMethods = HashSet.singleton (HashableMethod method_)
33+
in
34+
CORSOptions
35+
{ corsAllowOrigin = return allowedOrigins
36+
, corsAllowCredentials = return True
37+
, corsExposeHeaders = return HashSet.empty
38+
, corsAllowedMethods = return allowedMethods
39+
, corsAllowedHeaders = return
40+
}
41+
42+
43+
toOriginList :: [String] -> OriginList
44+
toOriginList origins =
45+
Origins $ mkOriginSet $
46+
case traverse parseURI origins of
47+
Just uris -> uris
48+
Nothing -> error "invalid entry given to toOriginList list"

0 commit comments

Comments
 (0)