packages feed

mdoc-0.5.0.0: src/Mdoc/Data/Page.hs

{-# OPTIONS_GHC -Wno-ambiguous-fields #-}

-- |
--
-- Module      : Mdoc.Data.Page
-- Copyright   : (c) 2026 Patrick Brisbin
-- License     : AGPL-3
-- Maintainer  : pbrisbin@gmail.com
-- Stability   : experimental
-- Portability : POSIX
module Mdoc.Data.Page
  ( Page

    -- * Adding to @SYNOPSIS@ / @DESCRIPTION@
  , addSwitch
  , addOption
  , addArgument
  , addAlternation
  , addCommands
  , addConfig

    -- * @ENVIRONMENT@
  , addEnvVar

    -- * Access necessary for building 'Command's
  , getSynopsis
  , getPositionals
  ) where

import Mdoc.Prelude

import Mdoc.Data.Argument
import Mdoc.Data.Command
import Mdoc.Data.Config
import Mdoc.Data.Described
import Mdoc.Data.EnvVar
import Mdoc.Data.List (ByItem, Indent, List)
import Mdoc.Data.List qualified as List
import Mdoc.Data.Optionality
import Mdoc.Data.Positional
import Mdoc.Data.Synopsis (Synopsis)
import Mdoc.Data.Synopsis qualified as Synopsis
import Mdoc.Optics

data Page = Page
  { synopsis :: Maybe Synopsis
  , options :: Maybe (List Indent (Described Positional))
  , commands :: Maybe (List Indent Command)
  , configs :: Maybe (List Indent (Described Config))
  , environment :: Maybe (List ByItem (Described EnvVar))
  }
  deriving stock (Eq, Generic, Show)
  deriving anyclass (ToJSON)
  deriving (Monoid, Semigroup) via Generically Page

addSwitch :: Described Flag -> Page -> Page
addSwitch = addPositional . fmap PositionalSwitch

addOption :: Described Option -> Page -> Page
addOption = addPositional . fmap PositionalOption

addArgument :: Described Argument -> Page -> Page
addArgument = addPositional . fmap PositionalArgument

addAlternation :: Optionality -> [Described Positional] -> Page -> Page
addAlternation o ps =
  appEndo
    $ mconcat
    $ Endo (field @"synopsis" <?>~ Synopsis.alternation o ps)
      : map (Endo . addPositionalOption) ps

addPositional :: Described Positional -> Page -> Page
addPositional p =
  (field @"synopsis" <?>~ Synopsis.singleton (fixPositional p))
    . addPositionalOption p

addPositionalOption :: Described Positional -> Page -> Page
addPositionalOption p = field @"options" <?>~ List.singleton (fixPositional p)

-- Avoid showing a bunch of (default: False) for switches
fixPositional :: Described Positional -> Described Positional
fixPositional described = case (described.item, described.optionality) of
  (PositionalSwitch {}, Defaulted {}) -> described {optionality = Optional}
  _ -> described

addCommands :: [Command] -> Page -> Page
addCommands cs =
  appEndo
    $ Endo (field @"synopsis" <?>~ Synopsis.command)
      <> foldMap addCommand cs
 where
  addCommand :: Command -> Endo Page
  addCommand c = Endo $ field @"commands" <?>~ List.singleton c

addConfig :: Described Config -> Page -> Page
addConfig described =
  field @"configs" <?>~ List.singleton described

addEnvVar :: Described EnvVar -> Page -> Page
addEnvVar described =
  field @"environment" <?>~ List.singleton described

getSynopsis :: Page -> Synopsis
getSynopsis p = fromMaybe mempty p.synopsis

getPositionals :: Page -> [Described Positional]
getPositionals p = maybe [] (toList . (.items)) p.options