packages feed

seihou-cli-0.4.0.0: src/Seihou/CLI/BrowseFormat.hs

module Seihou.CLI.BrowseFormat
  ( formatBrowseRegistry,
    formatBrowseSingleModule,
    formatBrowseSingleBlueprint,
    formatBrowseSinglePrompt,
    kindLabel,
  )
where

import Data.Text qualified as T
import Seihou.Core.Registry (EntryKind (..), Registry (..), RegistryEntry (..))
import Seihou.Core.Types (ModuleName (..))
import Seihou.Prelude

-- | Display-string label for an 'EntryKind'. Each label is padded to
-- eleven characters so registry rows line up regardless of which kind
-- appears on a given row.
kindLabel :: EntryKind -> Text
kindLabel ModuleEntry = "[module]   "
kindLabel RecipeEntry = "[recipe]   "
kindLabel BlueprintEntry = "[blueprint]"
kindLabel PromptEntry = "[prompt]   "

-- | Format browse output for a multi-module registry. Each row begins
-- with a per-kind label so the user can see at a glance whether they are
-- selecting a module, recipe, or blueprint.
formatBrowseRegistry :: Text -> Registry -> [(EntryKind, RegistryEntry)] -> Maybe Text -> Text
formatBrowseRegistry source registry filtered tagFilter =
  let header =
        registry.repoName
          <> "\n"
          <> maybe "" (<> "\n") registry.repoDescription
          <> "\n"
   in if null filtered
        then
          header
            <> ( case tagFilter of
                   Just tag -> "No entries matching tag '" <> tag <> "'.\n"
                   Nothing -> "No entries in registry.\n"
               )
        else
          let nameOf e = let (ModuleName n) = e.name in n
              maxNameLen = maximum (map (T.length . nameOf . snd) filtered)
              entryLines = T.unlines (map (formatEntry maxNameLen) filtered)
              n = length filtered
              noun = if n == 1 then "entry" else "entries"
              footer =
                T.pack (show n)
                  <> " "
                  <> noun
                  <> " available. Install with:\n"
                  <> "  seihou install "
                  <> source
                  <> " --module <name>\n"
                  <> "  seihou install "
                  <> source
                  <> " --all\n"
           in header <> "Available entries:\n\n" <> entryLines <> "\n" <> footer

-- | Format browse output for a single-module repo.
formatBrowseSingleModule :: Text -> Text -> Maybe Text -> Text
formatBrowseSingleModule source name desc =
  name
    <> "\n"
    <> maybe "" (\d -> "  " <> d <> "\n") desc
    <> "\n"
    <> "Single-module repository. Install with:\n"
    <> "  seihou install "
    <> source
    <> "\n"

-- | Format browse output for a single-blueprint repo.
formatBrowseSingleBlueprint :: Text -> Text -> Maybe Text -> Text
formatBrowseSingleBlueprint source name desc =
  name
    <> "\n"
    <> maybe "" (\d -> "  " <> d <> "\n") desc
    <> "\n"
    <> "Single-blueprint repository. Install with:\n"
    <> "  seihou install "
    <> source
    <> "\n"

-- | Format browse output for a single-prompt repo.
formatBrowseSinglePrompt :: Text -> Text -> Maybe Text -> Text
formatBrowseSinglePrompt source name desc =
  name
    <> "\n"
    <> maybe "" (\d -> "  " <> d <> "\n") desc
    <> "\n"
    <> "Single-prompt repository. Install with:\n"
    <> "  seihou install "
    <> source
    <> "\n"

formatEntry :: Int -> (EntryKind, RegistryEntry) -> Text
formatEntry maxNameLen (kind, entry) =
  let (ModuleName name) = entry.name
      padding = T.replicate (maxNameLen - T.length name + 3) " "
      desc = maybe "" id entry.description
      tagsText =
        if null entry.tags
          then ""
          else "  [" <> T.intercalate ", " entry.tags <> "]"
   in "  " <> kindLabel kind <> "  " <> name <> padding <> desc <> tagsText