packages feed

hs-bindgen-1.0.0.0: app/HsBindgen/Cli/Info/IncludeGraph.hs

-- | @hs-bindgen-cli info include-graph@ command
--
-- Intended for qualified import.
--
-- > import HsBindgen.Cli.Info.IncludeGraph qualified as IncludeGraph
module HsBindgen.Cli.Info.IncludeGraph (
    -- * CLI help
    info
    -- * Options
  , Opts(..)
  , parseOpts
    -- * Execution
  , exec
  ) where

import Data.Either (partitionEithers)
import Options.Applicative hiding (info)

import HsBindgen
import HsBindgen.App
import HsBindgen.ArtefactM
import HsBindgen.Backend.Category
import HsBindgen.Config
import HsBindgen.Config.Internal (BindgenConfig)
import HsBindgen.Frontend.Analysis.IncludeGraph (HeaderLabelStyle (..),
                                                 IncludeGraphFormat (..))
import HsBindgen.Frontend.Predicate
import HsBindgen.Imports
import HsBindgen.IR.C qualified as C
import HsBindgen.Macro

{-------------------------------------------------------------------------------
  CLI help
-------------------------------------------------------------------------------}

info :: InfoMod a
info = progDesc "Output the include graph"

{-------------------------------------------------------------------------------
  Options
-------------------------------------------------------------------------------}

data Opts = Opts {
      config         :: Config
    , predicate      :: Boolean Regex
    , labelStyle     :: HeaderLabelStyle
    , format         :: IncludeGraphFormat
    , output         :: Maybe FilePath
    , inputs         :: [C.UncheckedRootDirective]
    , filePolicy     :: FilePolicy
    , dirPolicy      :: DirPolicy
    }

parseOpts :: Parser Opts
parseOpts =
    Opts
      <$> parseConfig
      <*> parsePredicate
      <*> parseHeaderLabelStyle
      <*> parseIncludeGraphFormat
      <*> optional parseOutput'
      <*> parseInputs
      <*> parseFilePolicy
      <*> parseDirPolicy

parsePredicate :: Parser (Boolean Regex)
parsePredicate = fmap merge . many . asum $ [
      fmap (Right . BIf) $ strOption $ mconcat [
          long "include"
        , metavar "PCRE"
        , help "Only include headers with paths that match PCRE (by default, include all)"
        ]
    , fmap (Left . BIf) $ strOption $ mconcat [
          long "exclude"
        , metavar "PCRE"
        , help "Exclude headers with paths that match PCRE"
        ]
    ]
  where
    merge :: Eq a => [Either (Boolean a) (Boolean a)] -> Boolean a
    merge = uncurry mergeBooleans . fmap defaultIncludeAll . partitionEithers

    defaultIncludeAll :: [Boolean a] -> [Boolean a]
    defaultIncludeAll = \case
      [] -> [BTrue]
      xs -> xs

parseHeaderLabelStyle :: Parser HeaderLabelStyle
parseHeaderLabelStyle = flag ShowIncludeArgs ShowPaths $ mconcat [
      long "show-paths"
    , help "Show paths of include header files instead of their '#include' arguments"
    ]

parseIncludeGraphFormat :: Parser IncludeGraphFormat
parseIncludeGraphFormat = flag Mermaid SortedList $ mconcat [
      long "toposort"
    , help "Output a topologically sorted list of headers instead of a Mermaid graph"
    ]

parseOutput' :: Parser FilePath
parseOutput' = strOption $ mconcat [
      short 'o'
    , long "output"
    , metavar "PATH"
    , help "Output path for the graph"
    ]

{-------------------------------------------------------------------------------
  Execution
-------------------------------------------------------------------------------}

exec :: GlobalOpts -> Opts -> IO ()
exec global opts =
    hsBindgen
      global.unsafe
      global.safe
      bindgenConfig
      opts.inputs
      artefact
  where
    artefact :: Artefact CExpr ()
    artefact =
      writeIncludeGraph
        opts.predicate
        opts.labelStyle
        opts.format
        opts.filePolicy
        opts.dirPolicy
        opts.output

    bindgenConfig :: BindgenConfig
    bindgenConfig =
        toBindgenConfig
          opts.config
          (UniqueId       "unused-unique-id")
          (BaseModuleName "unused-module-name")
          (def :: ByCategory Choice)