@@ -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
+ dynamic `shouldHaveLayout` expectedDynamicLayout
114
+ it " nubs capture hints when equal" $ do
115
+ dynamicSameType `shouldHaveLayout` expectedDynamicSameTypeLayout
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,90 @@ 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
+ -- The expected representation of the Dynamic API layout.
401
+ --
402
+ expectedDynamicLayout :: Text
403
+ expectedDynamicLayout =
404
+ " /\n \
405
+ \└─ a/\n \
406
+ \ └─ <foo::Int|bar::Bool|baz::Char>/\n \
407
+ \ ├─ b/\n \
408
+ \ │ └─•\n \
409
+ \ ├─ c/\n \
410
+ \ │ └─•\n \
411
+ \ └─ d/\n \
412
+ \ └─•\n "
413
+
414
+ -- The same Dynamic API as above, except that the captured
415
+ -- values have the same hints
416
+ type DynamicSameType =
417
+ " a" :> Capture " foo" Int :> " b" :> End
418
+ :<|> " a" :> Capture " foo" Int :> " c" :> End
419
+ :<|> " a" :> Capture " foo" Int :> " d" :> End
420
+
421
+ dynamicSameType :: Proxy DynamicSameType
422
+ dynamicSameType = Proxy
423
+
424
+ -- The expected representation of the DynamicSameType API layout.
425
+ --
426
+ expectedDynamicSameTypeLayout :: Text
427
+ expectedDynamicSameTypeLayout =
428
+ " /\n \
429
+ \└─ a/\n \
430
+ \ └─ <foo::Int>/\n \
431
+ \ ├─ b/\n \
432
+ \ │ └─•\n \
433
+ \ ├─ c/\n \
434
+ \ │ └─•\n \
435
+ \ └─ d/\n \
436
+ \ └─•\n "
437
+
438
+ -- An API with a CaptureAll part
439
+
440
+ type CaptureAllLayout = " a" :> CaptureAll " foos" Int :> End
441
+
442
+ captureAllLayout :: Proxy CaptureAllLayout
443
+ captureAllLayout = Proxy
444
+
445
+ -- The expected representation of the CaptureAllLayout API.
446
+ --
447
+ expectedCaptureAllLayout :: Text
448
+ expectedCaptureAllLayout =
449
+ " /\n \
450
+ \└─ a/\n \
451
+ \ └─ <foos::[Int]>/\n \
452
+ \ └─•\n "
0 commit comments