Skip to content

Commit 420d633

Browse files
committed
Better type errors for NamedRoutes without Generic
1 parent c382a1f commit 420d633

File tree

5 files changed

+27
-1
lines changed

5 files changed

+27
-1
lines changed

servant-client-core/src/Servant/Client/Core/HasClient.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ instance
850850
( forall n. GClient api n
851851
, HasClient m (ToServantApi api)
852852
, RunClient m
853+
, ErrorIfNoGeneric api
853854
)
854855
=> HasClient m (NamedRoutes api) where
855856
type Client m (NamedRoutes api) = api (AsClientT m)

servant-docs/src/Servant/Docs/Internal.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import qualified GHC.Generics as G
6161
import GHC.TypeLits
6262
import Servant.API
6363
import Servant.API.ContentTypes
64+
import Servant.API.TypeErrors
6465
import Servant.API.TypeLevel
6566
import Servant.API.Generic
6667

@@ -1154,7 +1155,10 @@ instance (ToAuthInfo (BasicAuth realm usr), HasDocs api) => HasDocs (BasicAuth r
11541155
authProxy = Proxy :: Proxy (BasicAuth realm usr)
11551156
action' = over authInfo (|> toAuthInfo authProxy) action
11561157

1157-
instance HasDocs (ToServantApi api) => HasDocs (NamedRoutes api) where
1158+
instance
1159+
( HasDocs (ToServantApi api)
1160+
, ErrorIfNoGeneric api
1161+
) => HasDocs (NamedRoutes api) where
11581162
docsFor Proxy = docsFor (Proxy :: Proxy (ToServantApi api))
11591163

11601164
-- ToSample instances for simple types

servant-server/src/Servant/Server/Internal.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,7 @@ instance
983983
( HasServer (ToServantApi api) context
984984
, forall m. Generic (api (AsServerT m))
985985
, forall m. GServer api m
986+
, ErrorIfNoGeneric api
986987
) => HasServer (NamedRoutes api) context where
987988

988989
type ServerT (NamedRoutes api) m = api (AsServerT m)

servant/src/Servant/API/TypeErrors.hs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE ConstraintKinds #-}
12
{-# LANGUAGE DataKinds #-}
23
{-# LANGUAGE PolyKinds #-}
34
{-# LANGUAGE TypeFamilies #-}
@@ -12,9 +13,11 @@ module Servant.API.TypeErrors (
1213
PartialApplication,
1314
NoInstanceFor,
1415
NoInstanceForSub,
16+
ErrorIfNoGeneric,
1517
) where
1618

1719
import Data.Kind
20+
import GHC.Generics (Generic(..))
1821
import GHC.TypeLits
1922

2023
-- | No instance exists for @tycls (expr :> ...)@ because
@@ -38,3 +41,19 @@ type Arity (ty :: k) = Arity' k
3841
type family Arity' (ty :: k) :: Nat where
3942
Arity' (_ -> ty) = 1 + Arity' ty
4043
Arity' _ = 0
44+
45+
-- see https://blog.csongor.co.uk/report-stuck-families/
46+
type ErrorIfNoGeneric routes = Break (NoGeneric routes :: Type) (Rep (routes ()))
47+
48+
data T1 a
49+
50+
type family Break err a :: Constraint where
51+
Break _ T1 = ((), ())
52+
Break _ a = ()
53+
54+
type family NoGeneric (routes :: Type -> Type) where
55+
NoGeneric routes = TypeError
56+
( 'Text "Named routes require a "
57+
':<>: 'ShowType Generic ':<>: 'Text " instance for "
58+
':<>: 'ShowType routes
59+
)

servant/src/Servant/Links.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,7 @@ instance GLinkConstraints routes a => GLink routes a where
613613
instance
614614
( HasLink (ToServantApi routes)
615615
, forall a. GLink routes a
616+
, ErrorIfNoGeneric routes
616617
) => HasLink (NamedRoutes routes) where
617618

618619
type MkLink (NamedRoutes routes) a = routes (AsLink a)

0 commit comments

Comments
 (0)