1 {-# LANGUAGE DeriveFunctor #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
4 module Webc.Layouter where
6 import Control.Applicative (Applicative (..))
7 import Data.Either (Either (..))
8 import Data.Eq (Eq (..))
9 import Data.Function (id, (.))
10 import Data.Functor (Functor (..), (<$>))
11 import Data.List qualified as List
12 import Data.Ord (Ord (..))
13 import Data.Semigroup (Semigroup (..))
14 import Data.Tree (Forest, Tree (..), drawForest)
15 import Network.URI.Slug as URI
16 import Symantic.Classes (Iso (..), IsoFunctor (..), ProductFunctor (..), SumFunctor ((<+>)))
17 import Text.Show (Show (..))
21 -- * The 'Layouter' interpreter
23 newtype Layouter a = Layouter
25 LayoutBranch (Forest (LayoutNode a))
29 layout :: Layouter a -> Forest (LayoutNode a)
30 layout = collapseLayoutBranch . unLayouter
32 layouter :: Layouter a -> Layouter a
35 {- | Keep the path in the 'Tree'
36 where to append new branches;
37 in reverse order to simplify appending.
39 type LayoutBranch = []
41 {- | Fold 'LayoutBranch' maintained for appending;
42 to be done when there is no more appending.
44 collapseLayoutBranch ::
45 LayoutBranch (Forest (LayoutNode a)) ->
47 collapseLayoutBranch =
49 (\ts acc -> ((\(Node n ns) -> Node n (ns <> acc)) <$> ts))
52 instance Show a => Show (Layouter a) where
53 show = drawForest . ((show <$>) <$>) . layout
55 instance IsoFunctor Layouter where
56 Iso{..} <%> x = a2b <$> x
58 -- ** Type 'LayoutNode'
60 = LayoutNodeSlug a [Slug]
61 deriving (Eq, Ord, Show, Functor)
62 instance Applicative LayoutNode where
63 pure a = LayoutNodeSlug a []
64 LayoutNodeSlug f fs <*> LayoutNodeSlug a as =
65 LayoutNodeSlug (f a) (fs <> as)
67 layoutNode :: LayoutNode a -> Layouter a
68 layoutNode = Layouter . pure . pure . pure
70 instance ProductFunctor Layouter where
71 Layouter x <.> Layouter y =
92 instance SumFunctor Layouter where
95 [ collapseLayoutBranch (unLayouter (Left <$> x))
96 <> collapseLayoutBranch (unLayouter (Right <$> y))
98 instance Slugable Layouter where
99 literalSlug = layoutNode . LayoutNodeSlug () . pure
100 captureSlug n = layoutNode (LayoutNodeSlug placeholder [placeholder])
102 placeholder :: URI.Slug
103 placeholder = URI.decodeSlug ("<" <> (URI.encodeSlug n) <> ">")