Skip to content

Commit 4f12bda

Browse files
committed
Upgrade pretty printer
1 parent c95a39a commit 4f12bda

File tree

13 files changed

+246
-188
lines changed

13 files changed

+246
-188
lines changed

.github/workflows/ci.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ jobs:
1515
- uses: actions/checkout@v2
1616

1717
- uses: haskell/actions/setup@v1
18+
name: Setup Haskell ${{ matrix.ghc }} with Stack ${{ matrix.stack }}
1819
with:
1920
ghc-version: ${{ matrix.ghc }}
2021
# cabal-version: 'latest'. Omitted, but defalts to 'latest'
2122
enable-stack: true
2223
stack-version: ${{ matrix.stack }}
2324
stack-setup-ghc: true
2425

25-
- run: stack build
26+
- run: stack test
27+
name: Test

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ There is also built-in support for pretty printing schemas:
135135
```haskell
136136
import Data.Schema.PrettyPrint
137137

138-
putSchema personSchema
138+
putSchema' personSchema
139139
```
140140

141141
That will produce an output similar to the following:
@@ -156,7 +156,7 @@ Not happy with that? What about a pretty printer based on the given schema? Just
156156

157157
```haskell
158158
pprintPerson :: Person -> IO ()
159-
pprintPerson = prettyPrinter personSchema
159+
pprintPerson = prettyPrinter' personSchema
160160
```
161161

162162
## Credits

hschema-aeson/src/Data/Schema/JSON/Internal/Serializer.hs

+9-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
{-# LANGUAGE GADTs #-}
44
{-# LANGUAGE LambdaCase #-}
55
{-# LANGUAGE TypeOperators #-}
6-
{-# LANGUAGE TypeSynonymInstances #-}
76
{-# LANGUAGE UndecidableInstances #-}
87

98
module Data.Schema.JSON.Internal.Serializer where
@@ -37,7 +36,7 @@ instance Functor JsonDeserializer where
3736

3837
instance Applicative JsonDeserializer where
3938
pure x = JsonDeserializer $ \_ -> pure x
40-
(JsonDeserializer l) <*> (JsonDeserializer r) = JsonDeserializer $ \x -> (l x) <*> (r x)
39+
(JsonDeserializer l) <*> (JsonDeserializer r) = JsonDeserializer $ \x -> l x <*> r x
4140

4241
class ToJsonSerializer s where
4342
toJsonSerializer :: s ~> JsonSerializer
@@ -76,7 +75,7 @@ toJsonSerializerAlg = wrapNT $ \case
7675
AliasSchema (JsonSerializer base) iso -> JsonSerializer $ \value -> base (view (re iso) value)
7776

7877
instance ToJsonSerializer p => ToJsonSerializer (Schema p) where
79-
toJsonSerializer schema = (cataNT toJsonSerializerAlg) (unwrapSchema schema)
78+
toJsonSerializer schema = cataNT toJsonSerializerAlg (unwrapSchema schema)
8079

8180
instance (ToJsonDeserializer p, ToJsonDeserializer q) => ToJsonDeserializer (Sum p q) where
8281
toJsonDeserializer (InL l) = toJsonDeserializer l
@@ -86,22 +85,22 @@ toJsonDeserializerAlg :: ToJsonDeserializer p => HAlgebra (SchemaF p) JsonDeseri
8685
toJsonDeserializerAlg = wrapNT $ \case
8786
PrimitiveSchema p -> toJsonDeserializer p
8887

89-
RecordSchema fields -> JsonDeserializer $ \json -> case json of
88+
RecordSchema fields -> JsonDeserializer $ \case
9089
JSON.Object obj -> runAp decodeField $ unwrapField fields
9190
where decodeField :: FieldDef o JsonDeserializer v -> JSON.Parser v
9291
decodeField (RequiredField name (JsonDeserializer deserial) _) = JSON.explicitParseField deserial obj name
9392
decodeField (OptionalField name (JsonDeserializer deserial) _) = JSON.explicitParseFieldMaybe deserial obj name
94-
other -> fail $ "Expected JSON Object but got: " ++ (show other)
93+
other -> fail $ "Expected JSON Object but got: " ++ show other
9594

96-
UnionSchema alts -> JsonDeserializer $ \json -> case json of
95+
UnionSchema alts -> JsonDeserializer $ \case
9796
JSON.Object obj -> head . catMaybes . NEL.toList $ fmap lookupParser alts
9897
where lookupParser :: AltDef JsonDeserializer a -> Maybe (JSON.Parser a)
9998
lookupParser (AltDef name (JsonDeserializer deserial) pr) = do
10099
altParser <- deserial <$> Map.lookup name obj
101-
return $ (view $ re pr) <$> altParser
102-
other -> fail $ "Expected JSON Object but got: " ++ (show other)
100+
return $ view (re pr) <$> altParser
101+
other -> fail $ "Expected JSON Object but got: " ++ show other
103102

104-
AliasSchema (JsonDeserializer base) iso -> JsonDeserializer $ \json -> (view iso) <$> (base json)
103+
AliasSchema (JsonDeserializer base) iso -> JsonDeserializer (fmap (view iso) . base)
105104

106105
instance ToJsonDeserializer p => ToJsonDeserializer (Schema p) where
107-
toJsonDeserializer schema = (cataNT toJsonDeserializerAlg) (unwrapSchema schema)
106+
toJsonDeserializer schema = cataNT toJsonDeserializerAlg (unwrapSchema schema)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
{-# LANGUAGE FlexibleInstances #-}
2-
{-# LANGUAGE GADTs #-}
3-
{-# LANGUAGE KindSignatures #-}
4-
{-# LANGUAGE LambdaCase #-}
5-
{-# LANGUAGE TypeSynonymInstances #-}
1+
{-# LANGUAGE FlexibleInstances #-}
2+
{-# LANGUAGE GADTs #-}
3+
{-# LANGUAGE KindSignatures #-}
4+
{-# LANGUAGE LambdaCase #-}
65

76
module Data.Schema.JSON.Internal.Types where
87

@@ -18,10 +17,10 @@ import Data.Schema.PrettyPrint
1817
import Data.Scientific
1918
import Data.Text (Text)
2019
import qualified Data.Text as T
21-
import Data.Text.Prettyprint.Doc ((<+>))
22-
import qualified Data.Text.Prettyprint.Doc as PP
2320
import Data.Vector (Vector)
2421
import qualified Data.Vector as Vector
22+
import Prettyprinter ((<+>))
23+
import qualified Prettyprinter as PP
2524
import qualified Test.QuickCheck as QC
2625
import qualified Test.QuickCheck.Gen as QC
2726
import Test.QuickCheck.Instances.Scientific ()
@@ -43,49 +42,45 @@ type JsonSchema = Schema JsonType
4342
type JsonField o a = Field JsonSchema o a
4443

4544
instance ToJsonSerializer JsonType where
46-
toJsonSerializer jType = JsonSerializer $ case (unmutu jType) of
45+
toJsonSerializer jType = JsonSerializer $ case unmutu jType of
4746
JsonNumber -> JSON.Number
4847
JsonText -> JSON.String
4948
JsonBool -> JSON.Bool
50-
JsonArray value -> \x ->
51-
JSON.Array $ fmap (runJsonSerializer . toJsonSerializer $ value) x
52-
JsonMap value -> \x ->
53-
JSON.Object $ Map.map (runJsonSerializer . toJsonSerializer $ value) x
49+
JsonArray value -> JSON.Array . fmap (runJsonSerializer . toJsonSerializer $ value)
50+
JsonMap value -> JSON.Object . Map.map (runJsonSerializer . toJsonSerializer $ value)
5451

5552
instance ToJsonDeserializer JsonType where
56-
toJsonDeserializer jType = JsonDeserializer $ case (unmutu jType) of
53+
toJsonDeserializer jType = JsonDeserializer $ case unmutu jType of
5754
JsonNumber -> parseJSON
5855
JsonText -> parseJSON
5956
JsonBool -> parseJSON
6057
JsonArray value -> \case
6158
JSON.Array arr -> traverse (runJsonDeserializer . toJsonDeserializer $ value) arr
62-
other -> fail $ "Expected a JSON array but got: " ++ (show other)
59+
other -> fail $ "Expected a JSON array but got: " ++ show other
6360
JsonMap value -> \case
6461
JSON.Object obj -> Map.foldrWithKey Map.insert Map.empty <$> traverse (runJsonDeserializer . toJsonDeserializer $ value) obj
65-
other -> fail $ "Expected a JSON object but got: " ++ (show other)
62+
other -> fail $ "Expected a JSON object but got: " ++ show other
6663

6764
instance ToGen JsonType where
68-
toGen jType = case (unmutu jType) of
65+
toGen jType = case unmutu jType of
6966
JsonNumber -> QC.arbitrary
70-
JsonText -> T.pack <$> (QC.listOf QC.chooseAny)
67+
JsonText -> T.pack <$> QC.listOf QC.chooseAny
7168
JsonBool -> QC.arbitrary :: (QC.Gen Bool)
7269
JsonArray value -> Vector.fromList <$> QC.listOf (toGen value)
73-
JsonMap value -> Map.fromList <$> (QC.listOf $ liftA2 ((,)) (T.pack <$> (QC.listOf QC.chooseAny)) (toGen value))
70+
JsonMap value -> Map.fromList <$> QC.listOf (liftA2 (,) (T.pack <$> QC.listOf QC.chooseAny) (toGen value))
7471

7572
instance ToSchemaDoc JsonType where
76-
toSchemaDoc jType = SchemaDoc $ case (unmutu jType) of
73+
toSchemaDoc settings jType = SchemaDoc $ case unmutu jType of
7774
JsonNumber -> PP.pretty "Number"
7875
JsonText -> PP.pretty "Text"
7976
JsonBool -> PP.pretty "Bool"
80-
JsonArray value -> PP.pretty "[" <> (getDoc . toSchemaDoc $ value) <> PP.pretty "]"
81-
JsonMap value -> PP.pretty "Map { Text ->" <+> (getDoc . toSchemaDoc $ value) <+> PP.pretty "}"
77+
JsonArray value -> PP.pretty "[" <> (getDoc . toSchemaDoc settings $ value) <> PP.pretty "]"
78+
JsonMap value -> PP.pretty "Map { Text ->" <+> (getDoc . toSchemaDoc settings $ value) <+> PP.pretty "}"
8279

8380
instance ToSchemaLayout JsonType where
84-
toSchemaLayout jType = SchemaLayout $ case (unmutu jType) of
81+
toSchemaLayout settings jType = SchemaLayout $ case unmutu jType of
8582
JsonNumber -> PP.unsafeViaShow
8683
JsonText -> PP.unsafeViaShow
8784
JsonBool -> PP.unsafeViaShow
88-
JsonArray value -> \x ->
89-
PP.vsep $ fmap (\v -> runSchemaLayout (toSchemaLayout value) v) $ Vector.toList x
90-
JsonMap value -> \x ->
91-
PP.vsep $ fmap (\(k,v) -> PP.pretty k <+> PP.pretty "->" <+> runSchemaLayout (toSchemaLayout value) v) $ Map.toList x
85+
JsonArray value -> PP.vsep . fmap (renderSchemaLayout (toSchemaLayout settings value)) . Vector.toList
86+
JsonMap value -> PP.vsep . fmap (\(k,v) -> PP.pretty k <+> PP.pretty "->" <+> renderSchemaLayout (toSchemaLayout settings value) v) . Map.toList
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"roles":[{"user":{}},{"admin":{"department":"bar","subordinateCount":4}}],"name":"foo","birthDate":12}
1+
{"roles":[{"user":{}},{"admin":{"subordinateCount":4,"department":"bar"}}],"name":"foo","birthDate":12}

hschema-prettyprinter/package.yaml

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ dependencies:
2828
- natural-transformation
2929
- lens
3030
- free
31-
- prettyprinter
31+
- prettyprinter >= 1.7.0 && < 1.8.0
3232
- prettyprinter-ansi-terminal
3333
- text
3434
- unordered-containers
3535
- vector
3636

3737
library:
3838
source-dirs: src
39+
40+
exposed-modules:
41+
- Data.Schema.PrettyPrint.Internal.Algebra
42+
- Data.Schema.PrettyPrint.Internal.Types
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,37 @@
1-
{-# LANGUAGE DeriveFunctor #-}
2-
{-# LANGUAGE FlexibleInstances #-}
3-
{-# LANGUAGE GADTs #-}
4-
{-# LANGUAGE LambdaCase #-}
5-
{-# LANGUAGE RankNTypes #-}
6-
{-# LANGUAGE ScopedTypeVariables #-}
7-
{-# LANGUAGE TypeOperators #-}
8-
{-# LANGUAGE TypeSynonymInstances #-}
1+
{-# LANGUAGE FlexibleInstances #-}
2+
{-# LANGUAGE GADTs #-}
3+
{-# LANGUAGE RankNTypes #-}
4+
{-# LANGUAGE ScopedTypeVariables #-}
95

106
module Data.Schema.PrettyPrint
11-
( SchemaDoc (..)
12-
, ToSchemaDoc (..)
7+
( module Data.Schema.PrettyPrint.Internal.Algebra
8+
, module Data.Schema.PrettyPrint.Internal.Types
139
, putSchema
14-
, SchemaLayout (..)
15-
, ToSchemaLayout (..)
10+
, putSchema'
1611
, prettyPrinter
12+
, prettyPrinter'
1713
) where
1814

19-
import Control.Applicative.Free
20-
import Control.Functor.HigherOrder
21-
import Control.Lens hiding (iso)
22-
import Control.Monad.State (State)
23-
import qualified Control.Monad.State as ST
24-
import Control.Natural
25-
import Data.Functor.Contravariant
26-
import Data.Functor.Contravariant.Divisible
27-
import Data.Functor.Sum
28-
import qualified Data.HashMap.Strict as Map
29-
import Data.List.NonEmpty (NonEmpty)
30-
import qualified Data.List.NonEmpty as NEL
31-
import Data.Maybe
32-
import Data.Schema.Internal.Types
33-
import Data.Text.Prettyprint.Doc ((<+>), (<>))
34-
import qualified Data.Text.Prettyprint.Doc as PP
35-
import qualified Data.Text.Prettyprint.Doc.Render.Terminal as PP
36-
import qualified Data.Vector as Vector
3715

38-
type AnsiDoc = PP.Doc PP.AnsiStyle
16+
import Data.Schema.PrettyPrint.Internal.Algebra
17+
import Data.Schema.PrettyPrint.Internal.Types
18+
import qualified Prettyprinter.Render.Terminal as PP
3919

40-
indentAmount :: Int
41-
indentAmount = 2
42-
43-
doubleColon :: AnsiDoc
44-
doubleColon = PP.colon <> PP.colon
45-
46-
layoutFields :: forall o s. (forall v. FieldDef o s v -> AnsiDoc) -> Fields s o -> AnsiDoc
47-
layoutFields f fields = renderFields $ ST.execState (runAp fieldDoc $ unwrapField fields) []
48-
where fieldDoc :: FieldDef o s v -> State [AnsiDoc] v
49-
fieldDoc fld = do
50-
fieldDesc <- pure $ PP.pretty "*" <+> (PP.pretty $ fieldName fld) <+> (f fld)
51-
ST.modify $ \xs -> fieldDesc:xs
52-
return undefined
53-
54-
renderFields :: [AnsiDoc] -> AnsiDoc
55-
renderFields [] = PP.emptyDoc
56-
renderFields xs = PP.nest indentAmount $ PP.line <> PP.vsep xs
57-
58-
layoutAlts :: forall s o. (AltDef s o -> Maybe AnsiDoc) -> NonEmpty (AltDef s o) -> [AnsiDoc]
59-
layoutAlts f alts = catMaybes . NEL.toList $ altDoc <$> alts
60-
where altDoc :: AltDef s o -> Maybe AnsiDoc
61-
altDoc a = (\x -> PP.indent indentAmount $ PP.pretty "-" <+> (PP.pretty $ altName a) <> x) <$> (f a)
62-
63-
newtype SchemaDoc a = SchemaDoc { getDoc :: AnsiDoc } deriving Functor
64-
65-
instance Applicative SchemaDoc where
66-
pure _ = SchemaDoc $ PP.emptyDoc
67-
(SchemaDoc l) <*> (SchemaDoc r) = SchemaDoc $ l <> r
68-
69-
class ToSchemaDoc s where
70-
toSchemaDoc :: s ~> SchemaDoc
71-
72-
instance (ToSchemaDoc p, ToSchemaDoc q) => ToSchemaDoc (Sum p q) where
73-
toSchemaDoc (InL l) = toSchemaDoc l
74-
toSchemaDoc (InR r) = toSchemaDoc r
75-
76-
toSchemaDocAlg :: ToSchemaDoc s => HAlgebra (SchemaF s) SchemaDoc
77-
toSchemaDocAlg = wrapNT $ \case
78-
PrimitiveSchema p -> SchemaDoc $ doubleColon <+> (getDoc $ toSchemaDoc p)
79-
RecordSchema fields -> SchemaDoc $ layoutFields fieldDoc' fields
80-
where fieldDoc' :: FieldDef o SchemaDoc v -> AnsiDoc
81-
fieldDoc' (RequiredField _ schemaDoc _) = getDoc schemaDoc
82-
fieldDoc' (OptionalField _ schemaDoc _) = PP.pretty "?" <> (getDoc schemaDoc)
83-
UnionSchema alts -> SchemaDoc $ PP.vsep $ layoutAlts altDoc' alts
84-
where altDoc' :: AltDef SchemaDoc a -> Maybe AnsiDoc
85-
altDoc' (AltDef _ (SchemaDoc doc) _) = Just doc
86-
AliasSchema baseDoc _ -> SchemaDoc $ getDoc baseDoc
87-
88-
instance ToSchemaDoc s => ToSchemaDoc (Schema s) where
89-
toSchemaDoc schema = (cataNT toSchemaDocAlg) (unwrapSchema schema)
9020

9121
-- | Renders the given schema to the standard out
92-
putSchema :: ToSchemaDoc s => s a -> IO ()
93-
putSchema schema = do
94-
PP.putDoc . getDoc $ toSchemaDoc schema
22+
putSchema :: ToSchemaDoc s => LayoutSettings -> s a -> IO ()
23+
putSchema settings schema = do
24+
PP.putDoc . getDoc $ toSchemaDoc settings schema
9525
putStrLn ""
9626

97-
newtype SchemaLayout a = SchemaLayout { runSchemaLayout :: a -> AnsiDoc }
98-
99-
instance Contravariant SchemaLayout where
100-
contramap f (SchemaLayout g) = SchemaLayout $ g . f
101-
102-
instance Divisible SchemaLayout where
103-
conquer = SchemaLayout $ const PP.emptyDoc
104-
divide split leftLayout rightLayout = SchemaLayout $ \x ->
105-
let (left, right) = split x
106-
leftDoc = runSchemaLayout leftLayout left
107-
rightDoc = runSchemaLayout rightLayout right
108-
in leftDoc <+> PP.pretty "," <+> rightDoc
109-
110-
class ToSchemaLayout s where
111-
toSchemaLayout :: s ~> SchemaLayout
112-
113-
instance (ToSchemaLayout p, ToSchemaLayout q) => ToSchemaLayout (Sum p q) where
114-
toSchemaLayout (InL l) = toSchemaLayout l
115-
toSchemaLayout (InR r) = toSchemaLayout r
116-
117-
toSchemaLayoutAlg :: ToSchemaLayout s => HAlgebra (SchemaF s) SchemaLayout
118-
toSchemaLayoutAlg = wrapNT $ \case
119-
PrimitiveSchema p -> SchemaLayout $ \x -> PP.colon <+> runSchemaLayout (toSchemaLayout p) x
120-
RecordSchema fields -> SchemaLayout $ \rc -> layoutFields (fieldDocOf rc) fields
121-
where fieldDocOf :: o -> FieldDef o SchemaLayout v -> AnsiDoc
122-
fieldDocOf obj (RequiredField _ (SchemaLayout layout) getter) =
123-
let el = view getter obj
124-
in layout el
125-
fieldDocOf obj (OptionalField _ (SchemaLayout layout) getter) =
126-
let el = view getter obj
127-
in maybe (PP.pretty "Nothing") layout el
128-
UnionSchema alts -> SchemaLayout $ \value -> head $ layoutAlts (layoutAlt' value) alts
129-
where layoutAlt' :: o -> AltDef SchemaLayout o -> Maybe AnsiDoc
130-
layoutAlt' obj (AltDef _ (SchemaLayout layout) getter) = layout <$> obj ^? getter
131-
AliasSchema (SchemaLayout baseLayout) getter -> SchemaLayout $ \value -> baseLayout (view (re getter) value)
132-
133-
instance ToSchemaLayout s => ToSchemaLayout (Schema s) where
134-
toSchemaLayout schema = (cataNT toSchemaLayoutAlg) (unwrapSchema schema)
27+
putSchema' :: ToSchemaDoc s => s a -> IO ()
28+
putSchema' = putSchema defaultLayoutSettings
13529

13630
-- | Generates a renderer of data types based on the given schema
137-
prettyPrinter :: ToSchemaLayout s => s a -> (a -> IO ())
138-
prettyPrinter schema = \x -> do
139-
PP.putDoc $ runSchemaLayout (toSchemaLayout schema) x
31+
prettyPrinter :: ToSchemaLayout s => LayoutSettings -> s a -> (a -> IO ())
32+
prettyPrinter settings schema x = do
33+
PP.putDoc $ renderSchemaLayout (toSchemaLayout settings schema) x
14034
putStrLn ""
35+
36+
prettyPrinter' :: ToSchemaLayout s => s a -> (a -> IO ())
37+
prettyPrinter' = prettyPrinter defaultLayoutSettings

0 commit comments

Comments
 (0)