packages feed

mdoc-0.1.1.0: src/Mdoc/Gen/Described.hs

-- |
--
-- Module      : Mdoc.Gen.Described
-- Copyright   : (c) 2026 Patrick Brisbin
-- License     : AGPL-3
-- Maintainer  : pbrisbin@gmail.com
-- Stability   : experimental
-- Portability : POSIX
module Mdoc.Gen.Described
  ( Described (..)
  , required
  , redescribe
  , setHelpLines
  , setHelpText
  , partitionDescribed
  , renderDescribedItems
  , renderDescribedItem
  ) where

import Mdoc.Prelude

import Data.List (intersperse)
import Mdoc.Gen.Optionality
import Mdoc.MacroArg
import Mdoc.MacroName
import Mdoc.MdocLine
import Mdoc.Optics

data Described a = Described
  { item :: a
  , optionality :: Optionality
  , multiple :: Bool
  , helpLines :: Maybe (NonEmpty String)
  }
  deriving stock (Eq, Functor, Generic, Show)

-- | Describe an item as 'Required', singular, without help
required :: a -> Described a
required item =
  Described
    { item
    , optionality = Required
    , multiple = False
    , helpLines = Nothing
    }

-- | Concat a described list into a single item
--
-- NB. The element descriptions are discarded and 'required' is used. We could
-- get clever (e.g. any required -> required, concatenat help, etc) but it's
-- just not useful in the project to do so.
redescribe :: ([a] -> b) -> [Described a] -> Described b
redescribe f = required . f . map (.item)

setHelpLines :: NonEmpty String -> Described a -> Described a
setHelpLines x = field @"helpLines" ?~ x

-- | Set a 'Described's 'helpLines' from a 'Text'
setHelpText :: Text -> Described a -> Described a
setHelpText = maybe id setHelpLines . nonEmpty . lines . unpack

partitionDescribed :: [Described (Either a b)] -> ([Described a], [Described b])
partitionDescribed = go ([], [])
 where
  go acc [] = acc
  go (as, bs) (d : ds) = case d.item of
    Left a -> go (as <> [a <$ d], bs) ds
    Right b -> go (as, bs <> [b <$ d]) ds

renderDescribedItems
  :: Foldable t
  => (a -> [MacroArg])
  -> t (Described a)
  -> [MdocLine]
renderDescribedItems f = concatMap (renderDescribedItem f) . toList

-- TODO: add "Default:" line
-- TODO: add "This option may be specified multiple times" line
renderDescribedItem :: (a -> [MacroArg]) -> Described a -> [MdocLine]
renderDescribedItem f d = MacroLine It (f d.item) : descriptionLines
 where
  descriptionLines =
    intersperse (MacroLine Pp [])
      $ map (TextLine . esc . pack)
      $ maybe [] toList d.helpLines