@@ -1,3 +1,4 @@
1{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
@@ -19,8 +20,11 @@ import Elm.Json (jsonParserForType, jsonSerForType)
19import qualified Elm.Module as Elm
20import qualified Elm.Module as Elm
20import Elm.TyRep (ETCon(..), EType(..), ETypeDef(..), toElmType)
21import Elm.TyRep (ETCon(..), EType(..), ETypeDef(..), toElmType)
21import Elm.TyRender (renderElm)
22import Elm.TyRender (renderElm)
23+#if MIN_VERSION_elm_bridge(0,6,0)
24+import Elm.Versions (ElmVersion(Elm0p19))
22import Elm.Versions (ElmVersion(Elm0p18))
26import Elm.Versions (ElmVersion(Elm0p18))
24import Servant.Elm.Internal.Foreign (LangElm, getEndpoints)
28import Servant.Elm.Internal.Foreign (LangElm, getEndpoints)
25import qualified Servant.Foreign as F
29import qualified Servant.Foreign as F
26import System.Directory (createDirectoryIfMissing)
30import System.Directory (createDirectoryIfMissing)
@@ -49,16 +53,22 @@ data ElmOptions = ElmOptions
51 urlPrefix :: UrlPrefix
55 urlPrefix :: UrlPrefix
52− , elmTypeAlterations :: (EType -> EType)
56+ , elmTypeAlterations :: (EType -> EType)
53 -- ^ Alterations to perform on ETypes before code generation.
57 -- ^ Alterations to perform on ETypes before code generation.
54 , elmAlterations :: (ETypeDef -> ETypeDef)
58 , elmAlterations :: (ETypeDef -> ETypeDef)
55 -- ^ Alterations to perform on ETypeDefs before code generation.
59 -- ^ Alterations to perform on ETypeDefs before code generation.
56− , elmToString :: (EType -> Text)
60+ , elmToString :: (EType -> Text)
57 -- ^ Elm functions creating a string from a given type.
61 -- ^ Elm functions creating a string from a given type.
58 , emptyResponseElmTypes :: [EType]
62 , emptyResponseElmTypes :: [EType]
59 -- ^ Types that represent an empty Http response.
63 -- ^ Types that represent an empty Http response.
60 , stringElmTypes :: [EType]
64 , stringElmTypes :: [EType]
61 -- ^ Types that represent a String.
65 -- ^ Types that represent a String.
66+ , expectJsonMethod :: Text
67+ -- ^ The function to use for JSON responses.
68+ , expectStringMethod :: Text
69+ -- ^ The function to use for string responses.
70+ , httpErrorType :: Text
71+ -- ^ The type to use for Http errors.
@@ -82,6 +92,9 @@ The default options are:
82> , stringElmTypes =
92> , stringElmTypes =
83> [ getType (Proxy :: Proxy String)
93> [ getType (Proxy :: Proxy String)
84> , getType (Proxy :: Proxy T.Text) ]
94> , getType (Proxy :: Proxy T.Text) ]
95+> , expectJsonMethod = "Http.expectJson"
96+> , expectStringMethod = "Http.expectString"
97+> , httpErrorType = "Http.Error"
87defElmOptions :: ElmOptions
100defElmOptions :: ElmOptions
@@ -97,6 +110,9 @@ defElmOptions = ElmOptions
97 [ toElmType (Proxy :: Proxy String)
110 [ toElmType (Proxy :: Proxy String)
98 , toElmType (Proxy :: Proxy T.Text)
111 , toElmType (Proxy :: Proxy T.Text)
113+ , expectJsonMethod = "Http.expectJson"
114+ , expectStringMethod = "Http.expectString"
115+ , httpErrorType = "Http.Error"
@@ -115,6 +131,7 @@ The default required imports are:
115> import Set
131> import Set
116> import Http
132> import Http
117> import String
133> import String
118> import Url.Builder
135> import Url.Builder
120defElmImports :: Text
137defElmImports :: Text
@@ -128,6 +145,7 @@ defElmImports =
128 , "import Set"
145 , "import Set"
129 , "import Http"
146 , "import Http"
130 , "import String"
147 , "import String"
131 , "import Url.Builder"
149 , "import Url.Builder"
@@ -149,7 +167,12 @@ generateElmModuleWith ::
149generateElmModuleWith options namespace imports rootDir typeDefs api = do
167generateElmModuleWith options namespace imports rootDir typeDefs api = do
150 let out =
168 let out =
151 T.unlines $
169 T.unlines $
171+#if MIN_VERSION_elm_bridge(0,6,0)
172+ T.pack $ Elm.moduleHeader Elm0p19 moduleName
152− [ T.pack $ Elm.moduleHeader Elm0p18 moduleName
174+ T.pack $ Elm.moduleHeader Elm0p18 moduleName
154 , imports
177 , imports
155 , T.pack $ Elm.makeModuleContentWithAlterations (elmAlterations options) typeDefs
178 , T.pack $ Elm.makeModuleContentWithAlterations (elmAlterations options) typeDefs
@@ -236,7 +259,7 @@ generateElmForRequest opts request =
237 request ^. F.reqFuncName . to (replace . F.camelCase) . to stext
260 request ^. F.reqFuncName . to (replace . F.camelCase) . to stext
239− replace = T.replace "-" "" . T.replace "." ""
262+ replace = T.replace "-" "" . T.replace "." "" . T.replace " " ""
241 typeSignature =
264 typeSignature =
242 mkTypeSignature opts request
265 mkTypeSignature opts request
@@ -298,7 +321,7 @@ mkTypeSignature opts request =
298 toMsgType :: Maybe Doc
321 toMsgType :: Maybe Doc
299 toMsgType = do
322 toMsgType = do
300 result <- fmap elmTypeRef $ request ^. F.reqReturnType
323 result <- fmap elmTypeRef $ request ^. F.reqReturnType
301− Just ("(Result Http.Error " <+> parens result <+> " -> msg)")
324+ Just ("(Result" <+> stext (httpErrorType opts) <+> parens result <+> "-> msg)")
303 returnType :: Maybe Doc
326 returnType :: Maybe Doc
304 returnType = do
327 returnType = do
@@ -316,7 +339,7 @@ elmCaptureArg segment =
316 "capture_" <>
339 "capture_" <>
317 F.captureArg segment ^. F.argName . to (stext . replace . F.unPathSegment)
340 F.captureArg segment ^. F.argName . to (stext . replace . F.unPathSegment)
319− replace = T.replace "-" "_"
342+ replace = T.replace "-" "_" . T.replace " " "_"
322elmQueryArg :: F.QueryArg EType -> Doc
345elmQueryArg :: F.QueryArg EType -> Doc
@@ -420,7 +443,7 @@ mkLetParams opts request =
425 elmName = elmQueryArg qarg
448 elmName = elmQueryArg qarg
426 name = qarg ^. F.queryArgName . F.argName . to (stext . F.unPathSegment)
449 name = qarg ^. F.queryArgName . F.argName . to (stext . F.unPathSegment)
@@ -487,14 +510,14 @@ mkRequest opts request =
488 case request ^. F.reqReturnType of
511 case request ^. F.reqReturnType of
489 Just elmTypeExpr
512 Just elmTypeExpr
490− | isEmptyType opts elmTypeExpr
513+ | isEmptyType opts $ (elmTypeAlterations opts) elmTypeExpr
491 -- let elmConstructor = T.pack (renderElm elmTypeExpr)
514 -- let elmConstructor = T.pack (renderElm elmTypeExpr)
493− "Http.expectString " <> line <+> indent i "(\\x -> case x of" <> line <+>
516+ stext (expectStringMethod opts) <> line <+> indent i "(\\x -> case x of" <> line <+>
494 indent i "Err e -> toMsg (Err e)" <> line <+>
517 indent i "Err e -> toMsg (Err e)" <> line <+>
495 indent i "Ok _ -> toMsg (Ok ()))"
518 indent i "Ok _ -> toMsg (Ok ()))"
496 Just elmTypeExpr ->
519 Just elmTypeExpr ->
497− "Http.expectJson toMsg" <+> renderDecoderName elmTypeExpr
520+ stext (expectJsonMethod opts) <+> "toMsg" <+> renderDecoderName ((elmTypeAlterations opts) elmTypeExpr)
498 Nothing -> error "mkHttpRequest: no reqReturnType?"
521 Nothing -> error "mkHttpRequest: no reqReturnType?"
499 -- case request ^. F.reqReturnType of
522 -- case request ^. F.reqReturnType of
500 -- Just elmTypeExpr | isEmptyType opts elmTypeExpr ->
523 -- Just elmTypeExpr | isEmptyType opts elmTypeExpr ->
@@ -522,6 +545,8 @@ renderDecoderName elmTypeExpr =
522 parens ("Json.Decode.list " <> parens (renderDecoderName t))
545 parens ("Json.Decode.list " <> parens (renderDecoderName t))
523 ETyApp (ETyCon (ETCon "Maybe")) t ->
546 ETyApp (ETyCon (ETCon "Maybe")) t ->
524 parens ("Json.Decode.maybe " <> parens (renderDecoderName t))
547 parens ("Json.Decode.maybe " <> parens (renderDecoderName t))
549+ parens (renderDecoderName x <+> renderDecoderName y)
525 ETyCon (ETCon "Int") -> "Json.Decode.int"
550 ETyCon (ETCon "Int") -> "Json.Decode.int"
526 ETyCon (ETCon "String") -> "Json.Decode.string"
551 ETyCon (ETCon "String") -> "Json.Decode.string"
527 _ -> ("jsonDec" <> stext (T.pack (renderElm elmTypeExpr)))
552 _ -> ("jsonDec" <> stext (T.pack (renderElm elmTypeExpr)))
@@ -539,7 +564,7 @@ mkUrl opts segments =
540 urlBuilder :: Doc
565 urlBuilder :: Doc
541 urlBuilder = case urlPrefix opts of
566 urlBuilder = case urlPrefix opts of
542− Dynamic -> "Url.Builder.absolute" :: Doc
567+ Dynamic -> "Url.Builder.crossOrigin urlBase" :: Doc
543 Static url -> "Url.Builder.crossOrigin" <+> dquotes (stext url)
568 Static url -> "Url.Builder.crossOrigin" <+> dquotes (stext url)
545 segmentToDoc :: F.Segment EType -> Doc
570 segmentToDoc :: F.Segment EType -> Doc
@@ -549,10 +574,10 @@ mkUrl opts segments =
549 dquotes (stext (F.unPathSegment path))
574 dquotes (stext (F.unPathSegment path))
550 F.Cap arg ->
575 F.Cap arg ->
552− toStringSrc =
577+ toStringSrc =
553− toString opts (maybeOf (arg ^. F.argType))
578+ toString opts (elmTypeAlterations opts (maybeOf (arg ^. F.argType)))
555− pipeRight [elmCaptureArg s, toStringSrc]
580+ "Url.percentEncode " <> pipeRight [elmCaptureArg s, toStringSrc]
558mkQueryParams
583mkQueryParams
@@ -570,7 +595,7 @@ response body.
571isEmptyType :: ElmOptions -> EType -> Bool
596isEmptyType :: ElmOptions -> EType -> Bool
572isEmptyType opts elmTypeExpr =
597isEmptyType opts elmTypeExpr =
573− elmTypeExpr `elem` emptyResponseElmTypes opts
598+ (elmTypeAlterations opts elmTypeExpr) `elem` emptyResponseElmTypes opts
576{- | Determines whether we call `toString` on URL captures and query params of
601{- | Determines whether we call `toString` on URL captures and query params of
@@ -578,7 +603,7 @@ this type in Elm.
579isElmStringType :: ElmOptions -> EType -> Bool
604isElmStringType :: ElmOptions -> EType -> Bool
580isElmStringType opts elmTypeExpr =
605isElmStringType opts elmTypeExpr =
581− elmTypeExpr `elem` stringElmTypes opts
606+ (elmTypeAlterations opts elmTypeExpr) `elem` stringElmTypes opts
583{- | Determines whether a type is 'Maybe a' where 'a' is something akin to a 'String'.
608{- | Determines whether a type is 'Maybe a' where 'a' is something akin to a 'String'.
@@ -620,7 +645,7 @@ elmListOfMaybes ds = "List.filterMap identity" <$> indent 4 (elmList ds)
620defaultElmToString :: EType -> Text
645defaultElmToString :: EType -> Text
621defaultElmToString argType =
646defaultElmToString argType =
622 case argType of
647 case argType of
623− ETyCon (ETCon "Bool") -> "(\\value -> if value then \"1\" else \"0\")"
648+ ETyCon (ETCon "Bool") -> "(\\value -> if value then \"true\" else \"false\")"
624 ETyCon (ETCon "Float") -> "String.fromFloat"
649 ETyCon (ETCon "Float") -> "String.fromFloat"
625 ETyCon (ETCon "Char") -> "String.fromChar"
650 ETyCon (ETCon "Char") -> "String.fromChar"
626 ETyApp (ETyCon (ETCon "Maybe")) v -> "(Maybe.map " <> defaultElmToString v <> " >> Maybe.withDefault \"\")"
651 ETyApp (ETyCon (ETCon "Maybe")) v -> "(Maybe.map " <> defaultElmToString v <> " >> Maybe.withDefault \"\")"