@@ -13,21 +13,59 @@ module Yoga.Om.Layer
13 , runScopedWith
13 , runScopedWith
14 , withScoped
14 , withScoped
15 , withScopedWith
15 , withScopedWith
16 , combineRequirements
17 , combineRequirements
19 , recovering
21 , recovering
20 , repeating
22 , repeating
27+ , class FindProviderMatch
31+ , class BoxTop, class BoxMid, class BoxBot
32+ , class RenderDepChildNode
33+ , class IsNotProvided
34+ , class HasDirectMissing, class HasDirectMissingOr
35+ , class FindLayerReqs
36+ , class RenderProviderDeps
37+ , class RenderDepTree
38+ , class FirstProvLabel
39+ , class PrintLayerDispatch
40+ , class PrintLayersRL
42+ , class PrintLayersASCII
43+ , class PrintEdgesASCII
21 , class CheckAllProvided
45 , class CheckAllProvided
22 , class CheckAllLabelsExist
46 , class CheckAllLabelsExist
23 , class CheckLabelExists
47 , class CheckLabelExists
48+ , class TopoWire, topoWire
51+ , class FindReadyDispatch
26import Prelude
54import Prelude
56+import Control.Alt (class Alt)
57+import Control.Alt as Alt
58+import Control.Alternative (class Alternative)
28−import Control.Monad.Error.Class (catchError, throwError)
59+import Control.Monad.Error.Class (class MonadError, class MonadThrow, catchError, throwError)
60+import Control.Monad.Reader (class MonadAsk, class MonadReader)
61+import Control.Monad.Reader as Reader
62+import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM)
29import Control.Parallel (class Parallel)
63import Control.Parallel (class Parallel)
30import Control.Parallel as Parallel
64import Control.Parallel as Parallel
65+import Control.Plus (class Plus)
66+import Control.Plus as Plus
67+import Data.Symbol (class IsSymbol)
68+import Data.Variant (Variant)
31import Data.Array as Array
69import Data.Array as Array
32import Data.Foldable (for_)
70import Data.Foldable (for_)
33import Data.Map (Map)
71import Data.Map (Map)
@@ -38,20 +76,30 @@ import Data.Variant (class VariantMatchCases, onMatch)
38import Effect (Effect)
76import Effect (Effect)
39import Effect.Aff (Aff, bracket)
77import Effect.Aff (Aff, bracket)
40import Effect.Aff.Retry (RetryPolicyM, RetryStatus, applyAndDelay, defaultRetryStatus)
78import Effect.Aff.Retry (RetryPolicyM, RetryStatus, applyAndDelay, defaultRetryStatus)
79+import Effect.Aff.Class (class MonadAff)
80+import Effect.Aff.Class as Aff
41−import Effect.Class (liftEffect)
81+import Effect.Class (class MonadEffect, liftEffect)
82+import Effect.Class (class MonadEffect, liftEffect) as Effect
42import Effect.Exception (Error)
83import Effect.Exception (Error)
43import Effect.Ref (Ref)
84import Effect.Ref (Ref)
44import Effect.Ref as Ref
85import Effect.Ref as Ref
45import Effect.Unsafe (unsafePerformEffect)
86import Effect.Unsafe (unsafePerformEffect)
46import Prim.Row (class Nub, class Union)
87import Prim.Row (class Nub, class Union)
88+import Prim.Row as Row
89+import Type.Proxy (Proxy(..))
47import Prim.RowList (class RowToList, Cons, Nil, RowList)
90import Prim.RowList (class RowToList, Cons, Nil, RowList)
91+import Prim.Boolean (True, False)
92+import Prim.Symbol (class Append, class Cons)
93+import Type.Data.Boolean (class And)
94+import Type.RowList (class RowListAppend)
48−import Prim.TypeError (class Fail, Above, Quote, Text)
95+import Prim.TypeError (class Fail, class Warn, Above, Beside, Doc, Quote, Text)
49import Record as Record
96import Record as Record
50import Record.Studio (class Keys)
97import Record.Studio (class Keys)
51import Type.Equality (class TypeEquals)
98import Type.Equality (class TypeEquals)
52import Unsafe.Coerce (unsafeCoerce)
99import Unsafe.Coerce (unsafeCoerce)
53import Yoga.Om (Om, ParOm)
100import Yoga.Om (Om, ParOm)
54import Yoga.Om as Om
101import Yoga.Om as Om
102+import Yoga.Om.Error (Exception)
56-- =============================================================================
104-- =============================================================================
57-- Scope — a first-class representation of resource lifetime
105-- Scope — a first-class representation of resource lifetime
@@ -140,6 +188,36 @@ instance Bind (OmLayer req err) where
141instance Monad (OmLayer req err)
189instance Monad (OmLayer req err)
191+instance MonadEffect (OmLayer req err) where
192+ liftEffect eff = OmLayer (freshId unit) (Effect.liftEffect eff)
194+instance MonadAff (OmLayer req err) where
195+ liftAff aff = OmLayer (freshId unit) (Aff.liftAff aff)
197+instance MonadAsk (Record req) (OmLayer req err) where
198+ ask = OmLayer (freshId unit) Reader.ask
200+instance MonadReader (Record req) (OmLayer req err) where
201+ local f layer = OmLayer (freshId unit) (Reader.local f (buildLayer layer))
203+instance MonadThrow (Variant (Exception err)) (OmLayer req err) where
204+ throwError e = OmLayer (freshId unit) (throwError e)
206+instance MonadError (Variant (Exception err)) (OmLayer req err) where
207+ catchError layer handler = OmLayer (freshId unit) (catchError (buildLayer layer) (\e -> buildLayer (handler e)))
209+instance MonadRec (OmLayer req err) where
210+ tailRecM f a = OmLayer (freshId unit) (tailRecM (\x -> buildLayer (f x)) a)
212+instance Semigroup a => Semigroup (OmLayer req err a) where
213+ append a b = OmLayer (freshId unit) (append (buildLayer a) (buildLayer b))
215+instance Alt (OmLayer req err) where
216+ alt a b = OmLayer (freshId unit) (Alt.alt (buildLayer a) (buildLayer b))
218+instance Plus (OmLayer req err) where
219+ empty = OmLayer (freshId unit) Plus.empty
143instance Functor (ParOmLayer req err) where
221instance Functor (ParOmLayer req err) where
144 map f (ParOmLayer p) = ParOmLayer (map f p)
222 map f (ParOmLayer p) = ParOmLayer (map f p)
@@ -149,6 +227,14 @@ instance Apply (ParOmLayer req err) where
149instance Applicative (ParOmLayer req err) where
227instance Applicative (ParOmLayer req err) where
150 pure a = ParOmLayer (pure a)
228 pure a = ParOmLayer (pure a)
230+instance Alt (ParOmLayer req err) where
231+ alt (ParOmLayer a) (ParOmLayer b) = ParOmLayer (Alt.alt a b)
233+instance Plus (ParOmLayer req err) where
234+ empty = ParOmLayer Plus.empty
236+instance Alternative (ParOmLayer req err)
152instance Parallel (ParOmLayer req err) (OmLayer req err) where
238instance Parallel (ParOmLayer req err) (OmLayer req err) where
153 parallel (OmLayer _ om) = ParOmLayer (Parallel.parallel om)
239 parallel (OmLayer _ om) = ParOmLayer (Parallel.parallel om)
154 sequential (ParOmLayer p) = OmLayer (freshId unit) (Parallel.sequential p)
240 sequential (ParOmLayer p) = OmLayer (freshId unit) (Parallel.sequential p)
@@ -306,9 +392,13 @@ else instance
306-- =============================================================================
392-- =============================================================================
308-- | Run a layer with a given context, with custom error if requirements aren't met.
394-- | Run a layer with a given context, with custom error if requirements aren't met.
395+-- | `runLayer` requires a closed requirement row. If you have an open layer like
396+-- | `OmLayer (db :: Db | r) err a`, specialize it to a closed row before running
310 :: forall req err a available
399 :: forall req err a available
311 . CheckAllProvided req available
400 . CheckAllProvided req available
312 => Record available
402 => Record available
313 -> OmLayer req err a
403 -> OmLayer req err a
314 -> Om (Record available) err a
404 -> Om (Record available) err a
@@ -336,7 +426,7 @@ withScoped
336 . OmLayer (scope :: Scope) () (Record prov)
426 . OmLayer (scope :: Scope) () (Record prov)
337 -> (Record prov -> Aff a)
427 -> (Record prov -> Aff a)
339−withScoped = withScopedWith { exception: \_ -> pure (unsafeCoerce {}) }
429+withScoped = withScopedWith { exception: throwError }
341-- | Like `runScoped` but accepts error handlers for layers with typed errors.
431-- | Like `runScoped` but accepts error handlers for layers with typed errors.
342runScopedWith
432runScopedWith
@@ -369,12 +459,23 @@ withScopedWith handlers layer callback = bracket acquire release use
369 release { scope } = closeScope scope
459 release { scope } = closeScope scope
370 use { prov } = callback prov
460 use { prov } = callback prov
463+ :: forall prov a r rl err_ err
464+ . RowToList (exception :: Error -> Aff (Record prov) | r) rl
465+ => VariantMatchCases rl err_ (Aff (Record prov))
466+ => Union err_ () (exception :: Error | err)
467+ => { exception :: Error -> Aff (Record prov) | r }
468+ -> OmLayer (scope :: Scope) err (Record prov)
469+ -> (Record prov -> Aff a)
471+scoped = withScopedWith
372-- =============================================================================
473-- =============================================================================
373-- Horizontal composition — combine two layers with automatic deduplication
474-- Horizontal composition — combine two layers with automatic deduplication
374-- =============================================================================
475-- =============================================================================
376combineRequirements
477combineRequirements
377− :: forall req1 req2 prov1 prov2 err1 err2 provMerged provMergedRaw reqMerged reqDeduped errMerged errDeduped _req1 _req2 _err1 _err2
478+ :: forall req1 req2 prov1 prov2 err1 err2 provMerged reqMerged reqDeduped errMerged errDeduped _req1 _req2 _err1 _err2
378 . Union req1 req2 reqMerged
479 . Union req1 req2 reqMerged
379 => Nub reqMerged reqDeduped
480 => Nub reqMerged reqDeduped
380 => Union req1 _req1 reqDeduped
481 => Union req1 _req1 reqDeduped
@@ -383,8 +484,8 @@ combineRequirements
383 => Nub errMerged errDeduped
484 => Nub errMerged errDeduped
384 => Union err1 _err1 errDeduped
485 => Union err1 _err1 errDeduped
385 => Union err2 _err2 errDeduped
486 => Union err2 _err2 errDeduped
386− => Union prov1 prov2 provMergedRaw
487+ => Union prov1 prov2 provMerged
387− => Nub provMergedRaw provMerged
488+ => Nub provMerged provMerged
388 => Keys req1
489 => Keys req1
389 => Keys req2
490 => Keys req2
390 => OmLayer req1 err1 (Record prov1)
491 => OmLayer req1 err1 (Record prov1)
@@ -395,6 +496,17 @@ combineRequirements layer1 layer2 = OmLayer (freshId unit) do
395 rec2 <- Om.expand (buildLayer layer2)
496 rec2 <- Om.expand (buildLayer layer2)
396 pure (Record.merge rec1 rec2)
497 pure (Record.merge rec1 rec2)
500+ :: forall req err prov1 prov2 provMerged
501+ . Union prov1 prov2 provMerged
502+ => Nub provMerged provMerged
504+ -> OmLayer req err (Record prov2)
505+ -> OmLayer req err (Record provMerged)
506+withValue defaults layer = OmLayer (freshId unit) do
507+ rec <- buildLayer layer
508+ pure (Record.merge defaults rec)
398-- =============================================================================
510-- =============================================================================
399-- Vertical composition — feed output of one layer into input of another
511-- Vertical composition — feed output of one layer into input of another
400-- =============================================================================
512-- =============================================================================
@@ -464,3 +576,422 @@ repeating policy shouldRepeat (OmLayer layerId om) =
464 ifM (shouldRepeat status result)
576 ifM (shouldRepeat status result)
465 (applyAndDelay policy status >>= maybe (pure result) go)
577 (applyAndDelay policy status >>= maybe (pure result) go)
466 (pure result)
578 (pure result)
581+-- =============================================================================
582+-- Topological auto-wiring (order-independent)
583+-- =============================================================================
585+-- | Check if ALL labels in `needles` exist in `haystack`, returning a Boolean.
586+class AllLabelsIn (needles :: RowList Type) (haystack :: RowList Type) (result :: Boolean)
587+ | needles haystack -> result
589+instance AllLabelsIn Nil haystack True
592+ ( HasLabel label haystack found
593+ , AllLabelsIn tail haystack tailResult
594+ , And found tailResult result
596+ AllLabelsIn (Cons label ty tail) haystack result
598+-- | Dispatch based on whether a layer is ready (deps satisfied).
599+class FindReadyDispatch
603+ (tail :: RowList Type)
604+ (available :: RowList Type)
605+ (allLayersRL :: RowList Type)
606+ (foundSym :: Symbol)
607+ (foundLayer :: Type)
608+ (rest :: RowList Type)
609+ | ready sym layer tail available allLayersRL -> foundSym foundLayer rest
611+instance FindReadyDispatch True sym layer tail available allLayersRL sym layer tail
614+ FindReady tail available allLayersRL foundSym foundLayer rest' =>
615+ FindReadyDispatch False sym layer tail available allLayersRL foundSym foundLayer (Cons sym layer rest')
617+-- | Scan candidates for the first layer whose requirements are satisfied.
619+ (candidates :: RowList Type)
620+ (available :: RowList Type)
621+ (allLayersRL :: RowList Type)
624+ (rest :: RowList Type)
625+ | candidates available allLayersRL -> sym layer rest
628+ ( PrintLayersASCII allLayersRL graphDoc
631+ ( Above (Text "Dependency not provided!")
633+ ( Above (Text "No remaining layer has all its dependencies satisfied.")
635+ ( Above (Text "Dependency graph:") graphDoc
643+ FindReady Nil available allLayersRL sym layer rest
646+ ( RowToList req reqRL
647+ , AllLabelsIn reqRL available isReady
648+ , FindReadyDispatch isReady sym (OmLayer req err (Record prov)) tail available allLayersRL foundSym foundLayer rest
650+ FindReady (Cons sym (OmLayer req err (Record prov)) tail) available allLayersRL foundSym foundLayer rest
652+-- | Topologically sort and wire layers. Each iteration finds a layer whose
653+-- | dependencies are already satisfied, processes it, and adds its provisions.
655+ (remaining :: RowList Type)
656+ (layers :: Row Type)
657+ (accReq :: Row Type)
658+ (accErr :: Row Type)
659+ (accProv :: Row Type)
660+ (resReq :: Row Type)
661+ (resErr :: Row Type)
662+ (resProv :: Row Type)
663+ | remaining layers accReq accErr accProv -> resReq resErr resProv
668+ -> OmLayer accReq accErr (Record accProv)
669+ -> OmLayer resReq resErr (Record resProv)
671+instance TopoWire Nil layers accReq accErr accProv accReq accErr accProv where
672+ topoWire _ _ acc = acc
675+ ( RowToList layers allLayersRL
676+ , RowToList accProv accProvRL
677+ , RowToList accReq accReqRL
678+ , RowListAppend accProvRL accReqRL availableRL
679+ , FindReady remaining availableRL allLayersRL readySym (OmLayer nextReq nextErr (Record nextProv)) rest
681+ , Row.Cons readySym (OmLayer nextReq nextErr (Record nextProv)) _rest layers
682+ -- next's requirements must be subset of (accProv + accReq)
683+ , Union accProv accReq provReqOut
684+ , Nub provReqOut provReqOut
685+ , Union nextReq _nextReqRest provReqOut
690+ , Union accErr nextErr errMergedRaw
691+ , Nub errMergedRaw errMerged
692+ , Union accErr _e1 errMerged
693+ , Union nextErr _e2 errMerged
695+ , Union accProv nextProv provMerged
696+ , Nub provMerged provMerged
698+ , TopoWire rest layers accReq errMerged provMerged resReq resErr resProv
700+ TopoWire remaining layers accReq accErr accProv resReq resErr resProv where
701+ topoWire _ layers acc =
702+ topoWire (Proxy :: Proxy rest) layers combined
704+ next :: OmLayer nextReq nextErr (Record nextProv)
705+ next = Record.get (Proxy :: Proxy readySym) layers
707+ combined :: OmLayer accReq errMerged (Record provMerged)
708+ combined = OmLayer (freshId unit) do
709+ accProv <- Om.expandErr (buildLayer acc)
710+ nextProv <- Om.expand (buildLayer next) # Om.widenCtx accProv
711+ pure (Record.merge accProv nextProv)
714+ :: forall layers rl req err prov
715+ . RowToList layers rl
716+ => TopoWire rl layers (scope :: Scope) () () req err prov
718+ -> OmLayer req err (Record prov)
720+ topoWire (Proxy :: Proxy rl) layers seed
722+ seed = pure {} :: OmLayer (scope :: Scope) () (Record ())
724+-- =============================================================================
725+-- Debug graph printing (D2 diagram)
726+-- =============================================================================
728+-- | Remove `scope :: Scope` from a RowList (it's always present, just noise).
729+class FilterScope (rl :: RowList Type) (out :: RowList Type) | rl -> out
731+instance FilterScope Nil Nil
732+instance FilterScope (Cons "scope" Scope tail) tail
733+else instance FilterScope tail out => FilterScope (Cons sym ty tail) (Cons sym ty out)
735+-- | Marker symbol for unresolved dependencies.
736+type NotProvided :: Symbol
737+type NotProvided = "???"
739+-- | Given a label, find which layer in the RowList provides it.
740+class FindProvider (label :: Symbol) (rl :: RowList Type) (provider :: Symbol) | label rl -> provider
742+instance FindProvider label Nil NotProvided
745+ ( RowToList prov provRL
746+ , HasLabel label provRL hasIt
747+ , FindProviderMatch hasIt label sym (Cons sym (OmLayer req err (Record prov)) tail) provider
749+ FindProvider label (Cons sym (OmLayer req err (Record prov)) tail) provider
751+-- | Dispatch based on whether the current layer has the label.
752+class FindProviderMatch (hasIt :: Boolean) (label :: Symbol) (sym :: Symbol) (rl :: RowList Type) (provider :: Symbol) | hasIt label sym rl -> provider
754+instance FindProviderMatch True label sym rl sym
757+ FindProvider label tail provider =>
758+ FindProviderMatch False label sym (Cons sym ty tail) provider
760+-- | Check if a RowList contains a given label.
761+class HasLabel (label :: Symbol) (rl :: RowList Type) (result :: Boolean) | label rl -> result
763+instance HasLabel label Nil False
764+instance HasLabel label (Cons label ty tail) True
765+else instance HasLabel label tail result => HasLabel label (Cons sym ty tail) result
767+-- | Emit D2 edges for one consumer: "provider -> consumer" per requirement.
768+class EmitEdges (consumer :: Symbol) (reqRL :: RowList Type) (allLayers :: RowList Type) (doc :: Doc) | consumer reqRL allLayers -> doc
770+instance EmitEdges consumer Nil allLayers (Text "")
773+ ( FindProvider label allLayers provider
774+ , Append " " provider s0
775+ , Append s0 " --> " s1
776+ , Append s1 consumer line
777+ , EmitEdges consumer tail allLayers restDoc
779+ EmitEdges consumer (Cons label ty tail) allLayers (Above (Text line) restDoc)
781+-- | Walk all layers emitting D2 edges (mermaid format for wireLayersDebug).
782+class PrintLayersRL (rl :: RowList Type) (doc :: Doc) | rl -> doc
783+class PrintEdgesRL (rl :: RowList Type) (allLayers :: RowList Type) (doc :: Doc) | rl allLayers -> doc
785+instance PrintLayersRL Nil (Text "")
786+else instance PrintEdgesRL rl rl doc => PrintLayersRL rl doc
788+instance PrintEdgesRL Nil allLayers (Text "")
791+ ( RowToList req reqRL
792+ , FilterScope reqRL filteredReqRL
793+ , EmitEdges sym filteredReqRL allLayers edgeDoc
794+ , PrintEdgesRL tail allLayers restDoc
796+ PrintEdgesRL (Cons sym (OmLayer req err (Record prov)) tail) allLayers (Above edgeDoc restDoc)
798+-- | Map a symbol to same-length dashes (for box borders).
799+class DashesFor (sym :: Symbol) (dashes :: Symbol) | sym -> dashes
801+instance DashesFor "" ""
804+ ( Cons head tail sym
805+ , DashesFor tail rest
806+ , Append "─" rest dashes
808+ DashesFor sym dashes
810+-- | Build box parts from a symbol.
811+class BoxTop (sym :: Symbol) (result :: Symbol) | sym -> result
814+ ( DashesFor sym dashes
815+ , Append "╭─" dashes s0
816+ , Append s0 "─╮" result
820+class BoxMid (sym :: Symbol) (result :: Symbol) | sym -> result
823+ ( Append "│ " sym s0
824+ , Append s0 " │" result
828+class BoxBot (sym :: Symbol) (result :: Symbol) | sym -> result
831+ ( DashesFor sym dashes
832+ , Append "╰─" dashes s0
833+ , Append s0 "─╯" result
837+-- | Render a single dependency child node (dispatches on provided vs missing).
838+class RenderDepChildNode
839+ (isNotProvided :: Boolean) (provider :: Symbol) (label :: Symbol) (ty :: Type)
840+ (prePrefix :: Symbol) (contPrefix :: Symbol) (branchPrefix :: Symbol)
841+ (allLayers :: RowList Type) (doc :: Doc)
842+ | isNotProvided provider label ty prePrefix contPrefix branchPrefix allLayers -> doc
844+-- Missing dep: boxed label, type outside
846+ ( BoxTop label topLine, BoxMid label midLine, BoxBot label botLine
847+ , Append prePrefix topLine topFull
848+ , Append branchPrefix midLine midFull
849+ , Append contPrefix botLine botFull
851+ RenderDepChildNode True provider label ty prePrefix contPrefix branchPrefix allLayers
852+ (Above (Text topFull) (Above (Beside (Text midFull) (Beside (Text " :: ") (Beside (Quote ty) (Text " <-- ⚠️ not provided")))) (Text botFull)))
854+-- Provided dep: boxed label, then recurse into provider's sub-tree
856+ ( BoxTop label topLine, BoxMid label midLine, BoxBot label botLine
857+ , Append prePrefix topLine topFull
858+ , Append branchPrefix midLine midFull
859+ , Append contPrefix botLine botFull
860+ , Append contPrefix " " subTreePrefix
861+ , RenderProviderDeps provider subTreePrefix allLayers subDoc
863+ RenderDepChildNode False provider label ty prePrefix contPrefix branchPrefix allLayers
864+ (Above (Text topFull) (Above (Text midFull) (Above (Text botFull) subDoc)))
866+-- | Is this provider the NotProvided marker?
867+class IsNotProvided (provider :: Symbol) (result :: Boolean) | provider -> result
868+instance IsNotProvided NotProvided True
869+else instance IsNotProvided other False
871+-- | Does any requirement in this RowList resolve to NotProvided?
872+class HasDirectMissing (reqRL :: RowList Type) (allLayers :: RowList Type) (result :: Boolean)
873+ | reqRL allLayers -> result
875+instance HasDirectMissing Nil allLayers False
878+ ( FindProvider label allLayers provider
879+ , IsNotProvided provider isMissing
880+ , HasDirectMissingOr isMissing tail allLayers result
882+ HasDirectMissing (Cons label ty tail) allLayers result
884+class HasDirectMissingOr (found :: Boolean) (tail :: RowList Type) (allLayers :: RowList Type) (result :: Boolean)
885+ | found tail allLayers -> result
887+instance HasDirectMissingOr True tail allLayers True
890+ HasDirectMissing tail allLayers result =>
891+ HasDirectMissingOr False tail allLayers result
893+-- | Look up a layer's requirements by name.
894+class FindLayerReqs (sym :: Symbol) (allLayers :: RowList Type) (reqRL :: RowList Type)
895+ | sym allLayers -> reqRL
898+ RowToList req reqRL =>
899+ FindLayerReqs sym (Cons sym (OmLayer req err (Record prov)) tail) reqRL
902+ FindLayerReqs sym tail reqRL =>
903+ FindLayerReqs sym (Cons other otherLayer tail) reqRL
905+-- | Render a provider's sub-tree (stops at NotProvided or roots).
906+class RenderProviderDeps
907+ (provider :: Symbol) (prefix :: Symbol) (allLayers :: RowList Type) (doc :: Doc)
908+ | provider prefix allLayers -> doc
910+instance RenderProviderDeps NotProvided prefix allLayers (Text "")
913+ ( FindLayerReqs provider allLayers reqRL
914+ , FilterScope reqRL filteredReqRL
915+ , RenderDepTree prefix filteredReqRL allLayers doc
917+ RenderProviderDeps provider prefix allLayers doc
919+-- | Render a dependency tree with tree connectors.
921+ (prefix :: Symbol) (reqRL :: RowList Type) (allLayers :: RowList Type) (doc :: Doc)
922+ | prefix reqRL allLayers -> doc
924+instance RenderDepTree prefix Nil allLayers (Text "")
928+ ( FindProvider label allLayers provider
929+ , IsNotProvided provider isNP
930+ , Append prefix "│ " prePrefix
931+ , Append prefix " " contPrefix
932+ , Append prefix "└───" branchPrefix
933+ , RenderDepChildNode isNP provider label ty prePrefix contPrefix branchPrefix allLayers doc
935+ RenderDepTree prefix (Cons label ty Nil) allLayers doc
937+-- Non-last child: ├───
939+ ( FindProvider label allLayers provider
940+ , IsNotProvided provider isNP
941+ , Append prefix "│ " contPrefix
942+ , Append prefix "├───" branchPrefix
943+ , RenderDepChildNode isNP provider label ty contPrefix contPrefix branchPrefix allLayers childDoc
944+ , RenderDepTree prefix tail allLayers restDoc
946+ RenderDepTree prefix (Cons label ty tail) allLayers (Above childDoc restDoc)
948+-- | Extract the first label from a RowList.
949+class FirstProvLabel (rl :: RowList Type) (label :: Symbol) | rl -> label
950+instance FirstProvLabel (Cons label ty tail) label
952+-- | Dispatch: skip layers without missing deps, render tree for others.
953+class PrintLayerDispatch
954+ (show :: Boolean) (displayName :: Symbol) (filteredReqRL :: RowList Type)
955+ (tail :: RowList Type) (allLayers :: RowList Type) (doc :: Doc)
956+ | show displayName filteredReqRL tail allLayers -> doc
959+ PrintEdgesASCII tail allLayers doc =>
960+ PrintLayerDispatch False displayName filteredReqRL tail allLayers doc
963+ ( BoxTop displayName topLine, BoxMid displayName midLine, BoxBot displayName botLine
964+ , RenderDepTree " " filteredReqRL allLayers treeDoc
965+ , PrintEdgesASCII tail allLayers restDoc
967+ PrintLayerDispatch True displayName filteredReqRL tail allLayers
968+ (Above (Text topLine) (Above (Text midLine) (Above (Text botLine) (Above treeDoc (Above (Text "") restDoc)))))
970+-- | Walk all layers emitting ASCII dependency trees (for error messages).
971+class PrintLayersASCII (rl :: RowList Type) (doc :: Doc) | rl -> doc
972+class PrintEdgesASCII (rl :: RowList Type) (allLayers :: RowList Type) (doc :: Doc) | rl allLayers -> doc
974+instance PrintLayersASCII Nil (Text "")
975+else instance PrintEdgesASCII rl rl doc => PrintLayersASCII rl doc
977+instance PrintEdgesASCII Nil allLayers (Text "")
980+ ( RowToList req reqRL
981+ , FilterScope reqRL filteredReqRL
982+ , RowToList prov provRL
983+ , FirstProvLabel provRL displayName
984+ , HasDirectMissing filteredReqRL allLayers hasMissing
985+ , PrintLayerDispatch hasMissing displayName filteredReqRL tail allLayers doc
987+ PrintEdgesASCII (Cons sym (OmLayer req err (Record prov)) tail) allLayers doc
990+ :: forall layers rl req err prov doc
991+ . RowToList layers rl
992+ => PrintLayersRL rl doc
993+ => Warn (Above (Text "") (Above (Text "%%{init: {\"flowchart\": {\"defaultRenderer\": \"elk\"}} }%%") (Above (Text "flowchart LR") doc)))
994+ => TopoWire rl layers (scope :: Scope) () () req err prov
996+ -> OmLayer req err (Record prov)
997+wireLayersDebug = wireLayers