@@ -1,4 +1,5 @@
11{-# LANGUAGE QuantifiedConstraints #-}
2+
23module OpenAPI.Checker.TracePrefixTree
34 ( TracePrefixTree
45 , empty
@@ -8,65 +9,87 @@ module OpenAPI.Checker.TracePrefixTree
89 , foldWith
910 , toList
1011 , filter
11− ) where
12+ )
13+where
1214
13−import qualified GHC.Exts as T
15+import Data.Aeson
1416import Data.Foldable hiding (null, toList)
17+import qualified Data.HashMap.Strict as HM
1518import Data.Kind
1619import qualified Data.Map as M
1720import Data.Monoid
1821import qualified Data.Set as S
19−import qualified Data.TypeRepMap as T
22+import qualified Data.Text as T
23+import Data.Type.Equality
24+import qualified Data.TypeRepMap as TRM
25+import qualified Data.Vector as V
26+import qualified GHC.Exts as TRM
2027import OpenAPI.Checker.Trace
21−import Prelude hiding (null, filter)
2228import Type.Reflection
23−import Data.Type.Equality
29+import Prelude hiding (filter, null)
2430
2531-- | A list of @AnItem r f@, but optimized into a prefix tree.
2632data TracePrefixTree (f :: k -> Type) (r :: k) = TracePrefixTree
2733 { rootItems :: !(ASet (f r))
28− , snocItems :: !(T.TypeRepMap (AStep f r))
34+ , snocItems :: !(TRM.TypeRepMap (AStep f r))
2935 }
3036
37+instance (forall a. ToJSON (f a)) => ToJSON (TracePrefixTree f r) where
38+ toJSON =
39+ Object . getMergableObject
40+ . foldWith (\t x -> MergableObject . traceObject t $ toJSON x)
41+
3142deriving instance Eq (TracePrefixTree f a)
3243
3344-- Kind of orphan. Treat the map as an infinite tuple of @Maybe (f a)@'s, where
3445-- the components are ordered by the @SomeTypeRep@ of the @a@.
3546compareTRM
3647 :: (forall a. Typeable a => Ord (f a))
37− => T.TypeRepMap f -> T.TypeRepMap f -> Ordering
38−compareTRM s1 s2
39− = foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
48+ => TRM.TypeRepMap f
49+ -> TRM.TypeRepMap f
50+ -> Ordering
51+compareTRM s1 s2 =
52+ foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
4053 where
4154 (m1, m2) = (toMap s1, toMap s2)
4255 mKeys = S.toAscList $ M.keysSet m1 `S.union` M.keysSet m2
43− compareMaybe _ Nothing Nothing = EQ
44− compareMaybe _ Nothing (Just _) = LT
45− compareMaybe _ (Just _) Nothing = GT
56+ compareMaybe _ Nothing Nothing = EQ
57+ compareMaybe _ Nothing (Just _) = LT
58+ compareMaybe _ (Just _) Nothing = GT
4659 compareMaybe cmp (Just x) (Just y) = cmp x y
4760 compareW
4861 :: (forall a. Typeable a => Ord (f a))
49− => T.WrapTypeable f -> T.WrapTypeable f -> Ordering
50− compareW (T.WrapTypeable (x :: f a)) (T.WrapTypeable (y :: f b))
62+ => TRM.WrapTypeable f
63+ -> TRM.WrapTypeable f
64+ -> Ordering
65+ compareW (TRM.WrapTypeable (x :: f a)) (TRM.WrapTypeable (y :: f b))
5166 | Just Refl <- testEquality (typeRep @a) (typeRep @b) = compare x y
5267 | otherwise = EQ -- unreachable
53− toMap s = M.fromList
54− [(someTypeRep x, w) | w@(T.WrapTypeable x) <- T.toList s]
68+ toMap s =
69+ M.fromList
70+ [(someTypeRep x, w) | w@(TRM.WrapTypeable x) <- TRM.toList s]
5571
5672instance Ord (TracePrefixTree f a) where
57− compare (TracePrefixTree r1 s1) (TracePrefixTree r2 s2)
58− = compare r1 r2 <> compareTRM s1 s2
73+ compare (TracePrefixTree r1 s1) (TracePrefixTree r2 s2) =
74+ compare r1 r2 <> compareTRM s1 s2
5975
6076data ASet (a :: Type) where
6177 AnEmptySet :: ASet a
6278 ASet :: Ord a => S.Set a -> ASet a
6379
80+instance ToJSON a => ToJSON (ASet a) where
81+ toJSON =
82+ toJSON . \case
83+ AnEmptySet -> S.empty
84+ ASet s -> s
85+
6486instance Semigroup (ASet a) where
6587 AnEmptySet <> s = s
6688 s <> AnEmptySet = s
6789 ASet s1 <> ASet s2 = ASet $ S.union s1 s2
6890
6991deriving instance Eq (ASet a)
92+
7093deriving instance Ord (ASet a)
7194
7295-- type traceprefixset = traceprefixtree proxy
@@ -76,26 +99,33 @@ instance Monoid (ASet a) where
7699
77100data AStep (f :: k -> Type) (r :: k) (a :: k) where
78101 AStep
79− :: Steppable r a => !(M.Map (Step r a) (TracePrefixTree f a)) -> AStep f r a
102+ :: Steppable r a =>
103+ !(M.Map (Step r a) (TracePrefixTree f a))
104+ -> AStep f r a
80105
81106deriving instance Eq (AStep f r a)
107+
82108deriving instance Ord (AStep f r a)
83109
84110singleton :: AnItem f r -> TracePrefixTree f r
85−singleton (AnItem ys v) = go ys $ TracePrefixTree (ASet $ S.singleton v) T.empty
111+singleton (AnItem ys v) = go ys $ TracePrefixTree (ASet $ S.singleton v) TRM.empty
86112 where
87113 go :: Trace r a -> TracePrefixTree f a -> TracePrefixTree f r
88114 go Root !t = t
89− go (Snoc xs x) !t = go xs $ TracePrefixTree AnEmptySet $ T.one
90− $ AStep $ M.singleton x t
115+ go (Snoc xs x) !t =
116+ go xs $
117+ TracePrefixTree AnEmptySet $
118+ TRM.one $
119+ AStep $ M.singleton x t
91120
92121instance Semigroup (TracePrefixTree f r) where
93122 TracePrefixTree r1 s1 <> TracePrefixTree r2 s2 =
94− TracePrefixTree (r1 <> r2) (T.unionWith joinSteps s1 s2)
95− where joinSteps (AStep m1) (AStep m2) = AStep $ M.unionWith (<>) m1 m2
123+ TracePrefixTree (r1 <> r2) (TRM.unionWith joinSteps s1 s2)
124+ where
125+ joinSteps (AStep m1) (AStep m2) = AStep $ M.unionWith (<>) m1 m2
96126
97127instance Monoid (TracePrefixTree f r) where
98− mempty = TracePrefixTree mempty T.empty
128+ mempty = TracePrefixTree mempty TRM.empty
99129
100130empty :: TracePrefixTree f r
101131empty = mempty
@@ -104,12 +134,15 @@ fromList :: [AnItem f r] -> TracePrefixTree f r
104134fromList = foldMap singleton
105135
106136null :: TracePrefixTree f r -> Bool
107−null (TracePrefixTree AnEmptySet s) = T.size s == 0
137+null (TracePrefixTree AnEmptySet s) = TRM.size s == 0
108138null _ = False
109139
110140foldWith
111− :: forall f m r. Monoid m
112− => (forall a. Ord (f a) => Trace r a -> f a -> m) -> TracePrefixTree f r -> m
141+ :: forall f m r.
142+ Monoid m
143+ => (forall a. Ord (f a) => Trace r a -> f a -> m)
144+ -> TracePrefixTree f r
145+ -> m
113146foldWith k = goTPT Root
114147 where
115148 goTPT :: forall a. Trace r a -> TracePrefixTree f a -> m
@@ -117,17 +150,41 @@ foldWith k = goTPT Root
117150 goASet :: forall a. Trace r a -> ASet (f a) -> m
118151 goASet _ AnEmptySet = mempty
119152 goASet xs (ASet rs) = foldMap (k xs) rs
120− goTRM :: forall a. Trace r a -> T.TypeRepMap (AStep f a) -> m
121− goTRM xs s = foldMap (\(T.WrapTypeable f) -> goAStep xs f) $ T.toList s
153+ goTRM :: forall a. Trace r a -> TRM.TypeRepMap (AStep f a) -> m
154+ goTRM xs s = foldMap (\(TRM.WrapTypeable f) -> goAStep xs f) $ TRM.toList s
122155 goAStep :: forall a b. Trace r a -> AStep f a b -> m
123− goAStep xs (AStep m)
124− = M.foldrWithKey (\x t -> (goTPT (Snoc xs x) t <>)) mempty m
156+ goAStep xs (AStep m) =
157+ M.foldrWithKey (\x t -> (goTPT (Snoc xs x) t <>)) mempty m
125158
126159toList :: TracePrefixTree f r -> [AnItem f r]
127−toList t = appEndo (foldWith (\xs f -> Endo (AnItem xs f:)) t) []
160+toList t = appEndo (foldWith (\xs f -> Endo (AnItem xs f :)) t) []
128161
129162-- | Select a subtree by prefix
130163filter :: Trace r a -> TracePrefixTree f r -> TracePrefixTree f a
131164filter Root t = t
132−filter (Snoc xs x) t = foldMap (\(AStep m) -> fold $ M.lookup x m)
133− $ T.lookup $ snocItems $ filter xs t
165+filter (Snoc xs x) t =
166+ foldMap (\(AStep m) -> fold $ M.lookup x m) $
167+ TRM.lookup $ snocItems $ filter xs t
168+
169+newtype MergableObject = MergableObject {getMergableObject :: Object}
170+
171+instance Semigroup MergableObject where
172+ (MergableObject x) <> (MergableObject y) =
173+ MergableObject $ HM.unionWith mergeValue x y
174+ where
175+ mergeValue :: Value -> Value -> Value
176+ mergeValue (Object a) (Object b) =
177+ Object . getMergableObject $ MergableObject a <> MergableObject b
178+ mergeValue (Array a) (Array b) = Array $ a <> b
179+ mergeValue (Array a) b = Array $ V.snoc a b
180+ mergeValue a (Array b) = Array $ V.cons a b
181+ mergeValue a b = toJSON [a, b]
182+
183+instance Monoid MergableObject where
184+ mempty = MergableObject mempty
185+
186+traceObject :: Trace r a -> Value -> Object
187+traceObject Root (Object o) = o
188+traceObject Root v = HM.singleton "root" v
189+traceObject (root `Snoc` s) v =
190+ traceObject root . Object $ HM.singleton (T.pack . show $ s) v