Skip to content

Commit 38f519a

Browse files
authored
Merge pull request #1641 from amesgen/custom-error-for-named-routes-no-generic
Better type errors for `NamedRoutes` without `Generic` instance
2 parents a082794 + 420d633 commit 38f519a

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
@@ -860,6 +860,7 @@ instance
860860
( forall n. GClient api n
861861
, HasClient m (ToServantApi api)
862862
, RunClient m
863+
, ErrorIfNoGeneric api
863864
)
864865
=> HasClient m (NamedRoutes api) where
865866
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
@@ -1013,6 +1013,7 @@ instance
10131013
( HasServer (ToServantApi api) context
10141014
, forall m. Generic (api (AsServerT m))
10151015
, forall m. GServer api m
1016+
, ErrorIfNoGeneric api
10161017
) => HasServer (NamedRoutes api) context where
10171018

10181019
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
@@ -617,6 +617,7 @@ instance GLinkConstraints routes a => GLink routes a where
617617
instance
618618
( HasLink (ToServantApi routes)
619619
, forall a. GLink routes a
620+
, ErrorIfNoGeneric routes
620621
) => HasLink (NamedRoutes routes) where
621622

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

0 commit comments

Comments
 (0)