packages feed

salmon-ops-0.1.0.0: src/Salmon/Actions/Help.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}

module Salmon.Actions.Help where

import Control.Comonad.Cofree (Cofree)
import Data.Foldable (toList, traverse_)
import qualified Data.Maybe as Maybe
import GHC.Records

import Salmon.FoldBranch
import Salmon.Op.Actions
import Salmon.Op.Dag (Dag)
import qualified Salmon.Op.Dag as Dag
import Salmon.Op.Eval
import Salmon.Op.Graph
import Salmon.Op.OpGraph
import Salmon.Op.Ref (unRef)

import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.IO as Text

{- | Per-node path, as the segments (one 'shorthand' per non-'Actionless'
ancestor, root-to-node) that 'query' renders joined by @\/@ ((R4): @run
tree@\/@run dag@ moved to the computed 'Salmon.Op.Dag.Dag' and print
'printDagTree'\/'Salmon.Actions.Dot.printDagCograph' instead — this is
declared-graph-only now). Shared by 'printCograph', "Salmon.Actions.Dot", and
"Salmon.Actions.Query" so there is exactly one definition of "what a node's
path is" across all of them.
-}
nodeSegments ::
    (Functor t) =>
    Cofree t (Actions ext) ->
    Cofree t [Text]
nodeSegments = foldBranch step []
  where
    step pfx x =
        case x of
            Actionless -> pfx
            Actions y -> pfx <> [shorthand y]

pathText :: [Text] -> Text
pathText = ("" <>) . Text.concat . map ("/" <>)

printTree :: (Monad m) => (forall a. m a -> IO a) -> OpGraph m (Actions ext) -> IO ()
printTree nat graph = do
    printCograph =<< nat (expand graph)

printCograph ::
    (Monad m) =>
    Cofree Graph (OpGraph m (Actions ext)) ->
    IO ()
printCograph gr1 = do
    traverse_ Text.putStrLn $ dirtree gr1
  where
    dirtree = fmap pathText . nodeSegments . fmap node

printHelpTree ::
    ( Monad m
    , HasField "help" ext Text
    ) =>
    (forall a. m a -> IO a) ->
    OpGraph m (Actions ext) ->
    IO ()
printHelpTree nat graph = do
    printHelpCograph =<< nat (expand graph)

printHelpCograph ::
    ( Monad m
    , HasField "help" ext Text
    ) =>
    Cofree Graph (OpGraph m (Actions ext)) ->
    IO ()
printHelpCograph gr1 = do
    let as = toList $ dirtree gr1
    let bs = toList $ helptree gr1
    traverse_ Text.putStrLn $ zipWith (\a b -> a <> " " <> b) as bs
  where
    dirtree = fmap pathText . nodeSegments . fmap node
    helptree = fmap (helpnode . node)
    helpnode x =
        case x of
            Actionless -> ""
            (Actions act) -> (extension act).help

{- | (R4) 'printHelpCograph' for a folded, and possibly rewritten, 'Dag'
rather than the declared @Cofree Graph@ — what @run tree@ prints once any
"Salmon.Op.Rewrite" phases are registered, so a batched node is shown once,
under the shorthand\/help the rewrite gave it, rather than as however many
per-package nodes it replaced.

There is no path to print: a 'Dag' is 'Ref'-keyed, not tree-shaped, so a
node reached from several declarations no longer has several positions to
list it at — it is one line, same as it is one node in the traversal that
actually runs. Each line is followed by its dependencies, indented, so the
ordering a rewrite's edges impose (e.g. removals before installs) is still
visible without a hierarchy to draw it in.
-}
printDagTree ::
    (HasField "help" ext Text) =>
    Dag ext ->
    IO ()
printDagTree dag = traverse_ Text.putStrLn (dagLines dag)

dagLines :: (HasField "help" ext Text) => Dag ext -> [Text]
dagLines dag =
    [ line
    | aref <- Dag.dagOrder dag
    , Just act <- [Dag.representativeOf dag aref]
    , line <- nodeLine aref act : depLines aref
    ]
  where
    nodeLine aref act =
        act.shorthand <> " (" <> unRef aref <> ") " <> (extension act).help
    depLines aref =
        [ "  <- " <> Maybe.maybe (unRef dref) (.shorthand) (Dag.representativeOf dag dref)
        | dref <- Dag.dependenciesOf dag aref
        ]