@@ -29,6 +29,7 @@ spec :: Spec
29
29
spec = describe " Servant.Server.Internal.Router" $ do
30
30
routerSpec
31
31
distributivitySpec
32
+ serverLayoutSpec
32
33
33
34
routerSpec :: Spec
34
35
routerSpec = do
@@ -103,12 +104,30 @@ distributivitySpec =
103
104
it " properly handles mixing static paths at different levels" $ do
104
105
level `shouldHaveSameStructureAs` levelRef
105
106
107
+ serverLayoutSpec :: Spec
108
+ serverLayoutSpec =
109
+ describe " serverLayout" $ do
110
+ it " correctly represents the example API" $ do
111
+ exampleLayout `shouldHaveLayout` expectedExampleLayout
112
+ it " aggregates capture hints when different" $ do
113
+ captureDifferentTypes `shouldHaveLayout` expectedCaptureDifferentTypes
114
+ it " nubs capture hints when equal" $ do
115
+ captureSameType `shouldHaveLayout` expectedCaptureSameType
116
+ it " properly displays CaptureAll hints" $ do
117
+ captureAllLayout `shouldHaveLayout` expectedCaptureAllLayout
118
+
106
119
shouldHaveSameStructureAs ::
107
120
(HasServer api1 '[] , HasServer api2 '[] ) => Proxy api1 -> Proxy api2 -> Expectation
108
121
shouldHaveSameStructureAs p1 p2 =
109
122
unless (sameStructure (makeTrivialRouter p1) (makeTrivialRouter p2)) $
110
123
expectationFailure (" expected:\n " ++ unpack (layout p2) ++ " \n but got:\n " ++ unpack (layout p1))
111
124
125
+ shouldHaveLayout ::
126
+ (HasServer api '[] ) => Proxy api -> Text -> Expectation
127
+ shouldHaveLayout p l =
128
+ unless (routerLayout (makeTrivialRouter p) == l) $
129
+ expectationFailure (" expected:\n " ++ unpack l ++ " \n but got:\n " ++ unpack (layout p))
130
+
112
131
makeTrivialRouter :: (HasServer layout '[] ) => Proxy layout -> Router ()
113
132
makeTrivialRouter p =
114
133
route p EmptyContext (emptyDelayed (FailFatal err501))
@@ -344,3 +363,100 @@ level = Proxy
344
363
345
364
levelRef :: Proxy LevelRef
346
365
levelRef = Proxy
366
+
367
+ -- The example API for the 'layout' function.
368
+ -- Should get factorized by the 'choice' smart constructor.
369
+ type ExampleLayout =
370
+ " a" :> " d" :> Get '[JSON ] NoContent
371
+ :<|> " b" :> Capture " x" Int :> Get '[JSON ] Bool
372
+ :<|> " c" :> Put '[JSON ] Bool
373
+ :<|> " a" :> " e" :> Get '[JSON ] Int
374
+ :<|> " b" :> Capture " x" Int :> Put '[JSON ] Bool
375
+ :<|> Raw
376
+
377
+ exampleLayout :: Proxy ExampleLayout
378
+ exampleLayout = Proxy
379
+
380
+ -- The expected representation of the example API layout
381
+ --
382
+ expectedExampleLayout :: Text
383
+ expectedExampleLayout =
384
+ " /\n \
385
+ \├─ a/\n \
386
+ \│ ├─ d/\n \
387
+ \│ │ └─•\n \
388
+ \│ └─ e/\n \
389
+ \│ └─•\n \
390
+ \├─ b/\n \
391
+ \│ └─ <x::Int>/\n \
392
+ \│ ├─•\n \
393
+ \│ ┆\n \
394
+ \│ └─•\n \
395
+ \├─ c/\n \
396
+ \│ └─•\n \
397
+ \┆\n \
398
+ \└─ <raw>\n "
399
+
400
+ -- A capture API with all capture types being the same
401
+ --
402
+ type CaptureSameType =
403
+ " a" :> Capture " foo" Int :> " b" :> End
404
+ :<|> " a" :> Capture " foo" Int :> " c" :> End
405
+ :<|> " a" :> Capture " foo" Int :> " d" :> End
406
+
407
+ captureSameType :: Proxy CaptureSameType
408
+ captureSameType = Proxy
409
+
410
+ -- The expected representation of the CaptureSameType API layout.
411
+ --
412
+ expectedCaptureSameType :: Text
413
+ expectedCaptureSameType =
414
+ " /\n \
415
+ \└─ a/\n \
416
+ \ └─ <foo::Int>/\n \
417
+ \ ├─ b/\n \
418
+ \ │ └─•\n \
419
+ \ ├─ c/\n \
420
+ \ │ └─•\n \
421
+ \ └─ d/\n \
422
+ \ └─•\n "
423
+
424
+ -- A capture API capturing different types
425
+ --
426
+ type CaptureDifferentTypes =
427
+ " a" :> Capture " foo" Int :> " b" :> End
428
+ :<|> " a" :> Capture " bar" Bool :> " c" :> End
429
+ :<|> " a" :> Capture " baz" Char :> " d" :> End
430
+
431
+ captureDifferentTypes :: Proxy CaptureDifferentTypes
432
+ captureDifferentTypes = Proxy
433
+
434
+ -- The expected representation of the CaptureDifferentTypes API layout.
435
+ --
436
+ expectedCaptureDifferentTypes :: Text
437
+ expectedCaptureDifferentTypes =
438
+ " /\n \
439
+ \└─ a/\n \
440
+ \ └─ <foo::Int|bar::Bool|baz::Char>/\n \
441
+ \ ├─ b/\n \
442
+ \ │ └─•\n \
443
+ \ ├─ c/\n \
444
+ \ │ └─•\n \
445
+ \ └─ d/\n \
446
+ \ └─•\n "
447
+
448
+ -- An API with a CaptureAll part
449
+
450
+ type CaptureAllLayout = " a" :> CaptureAll " foos" Int :> End
451
+
452
+ captureAllLayout :: Proxy CaptureAllLayout
453
+ captureAllLayout = Proxy
454
+
455
+ -- The expected representation of the CaptureAllLayout API.
456
+ --
457
+ expectedCaptureAllLayout :: Text
458
+ expectedCaptureAllLayout =
459
+ " /\n \
460
+ \└─ a/\n \
461
+ \ └─ <foos::[Int]>/\n \
462
+ \ └─•\n "
0 commit comments