-- | Description for options
--   A option has a long name (unless it's an argument), a short name, a metavar (its type), a help text
module Data.Registry.Options.OptionDescription where

import Data.Registry.Options.Text
import Data.Text qualified as T
import Protolude as P

-- | Optional values used to document a command line option
--
--     - 'name' is a "long" name, like @launch-missiles@
--     - 'short' is just a character, like @l@
--     - 'metavar' describes the type of value which is expected, like @BOOL@
--     - 'help' is a piece of text describing the usage of the option, like @"destroy everything"@
data OptionDescription = OptionDescription
  { OptionDescription -> Maybe Text
_name :: Maybe Text,
    OptionDescription -> [Text]
_aliases :: [Text],
    OptionDescription -> Maybe Char
_shortName :: Maybe Char,
    OptionDescription -> Maybe Text
_metavar :: Maybe Text,
    OptionDescription -> Maybe Text
_help :: Maybe Text
  }
  deriving (OptionDescription -> OptionDescription -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: OptionDescription -> OptionDescription -> Bool
$c/= :: OptionDescription -> OptionDescription -> Bool
== :: OptionDescription -> OptionDescription -> Bool
$c== :: OptionDescription -> OptionDescription -> Bool
Eq, Int -> OptionDescription -> ShowS
[OptionDescription] -> ShowS
OptionDescription -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [OptionDescription] -> ShowS
$cshowList :: [OptionDescription] -> ShowS
show :: OptionDescription -> String
$cshow :: OptionDescription -> String
showsPrec :: Int -> OptionDescription -> ShowS
$cshowsPrec :: Int -> OptionDescription -> ShowS
Show)

-- | The Semigroup instance is used to collect several descriptions and
--   aggregate them together, for example name @"force" <> short \'f\'@
--
--   The second option always takes precedence on the first one
--   for example metavar @"m1" <> metavar "m2" == metavar "m2"@
instance Semigroup OptionDescription where
  OptionDescription Maybe Text
n1 [Text]
as1 Maybe Char
s1 Maybe Text
m1 Maybe Text
h1 <> :: OptionDescription -> OptionDescription -> OptionDescription
<> OptionDescription Maybe Text
n2 [Text]
as2 Maybe Char
s2 Maybe Text
m2 Maybe Text
h2 =
    Maybe Text
-> [Text]
-> Maybe Char
-> Maybe Text
-> Maybe Text
-> OptionDescription
OptionDescription (Maybe Text
n2 forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Text
n1) ([Text]
as1 forall a. Semigroup a => a -> a -> a
<> [Text]
as2) (Maybe Char
s2 forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Char
s1) (Maybe Text
m2 forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Text
m1) (Maybe Text
h2 forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Text
h1)

instance Monoid OptionDescription where
  mempty :: OptionDescription
mempty = Maybe Text
-> [Text]
-> Maybe Char
-> Maybe Text
-> Maybe Text
-> OptionDescription
OptionDescription forall a. Maybe a
Nothing forall a. Monoid a => a
mempty forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing
  mappend :: OptionDescription -> OptionDescription -> OptionDescription
mappend = forall a. Semigroup a => a -> a -> a
(<>)

-- | Function updating an OptionDescription
type OptionDescriptionUpdate = OptionDescription -> OptionDescription

-- | List of description updates
type OptionDescriptionUpdates = [OptionDescriptionUpdate]

-- | Apply a list of option description updates, from left to right,
--   to the empty description
makeOptionDescription :: OptionDescriptionUpdates -> OptionDescription
makeOptionDescription :: OptionDescriptionUpdates -> OptionDescription
makeOptionDescription = forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\OptionDescription
r OptionDescription -> OptionDescription
u -> OptionDescription -> OptionDescription
u OptionDescription
r) forall a. Monoid a => a
mempty

-- | Create an 'OptionDescriptionUpdate' with a long hyphenated name, for example @name "collect-all"@
name :: Text -> OptionDescriptionUpdate
name :: Text -> OptionDescription -> OptionDescription
name Text
t OptionDescription
o = OptionDescription
o {_name :: Maybe Text
_name = forall a. a -> Maybe a
Just Text
t}

-- | Create an 'OptionDescriptionUpdate' with an alias for a given name
alias :: Text -> OptionDescriptionUpdate
alias :: Text -> OptionDescription -> OptionDescription
alias Text
t OptionDescription
o = OptionDescription
o {_aliases :: [Text]
_aliases = [Text
t]}

-- | Create an 'OptionDescriptionUpdate' with a short name, for example @short \'q\'@
short :: Char -> OptionDescriptionUpdate
short :: Char -> OptionDescription -> OptionDescription
short Char
t OptionDescription
o = OptionDescription
o {_shortName :: Maybe Char
_shortName = forall a. a -> Maybe a
Just Char
t}

-- | Create an 'OptionDescriptionUpdate' with specifying that there must be no short name
noShort :: OptionDescriptionUpdate
noShort :: OptionDescription -> OptionDescription
noShort OptionDescription
o = OptionDescription
o {_shortName :: Maybe Char
_shortName = forall a. Maybe a
Nothing}

-- | Create an 'OptionDescriptionUpdate' with a metavar to indicate the type of an option, for example @metavar "FILE"@
metavar :: Text -> OptionDescriptionUpdate
metavar :: Text -> OptionDescription -> OptionDescription
metavar Text
t OptionDescription
o = OptionDescription
o {_metavar :: Maybe Text
_metavar = forall a. a -> Maybe a
Just Text
t}

-- | Create an 'OptionDescriptionUpdate' with some help text, for example @help "force the copy"@
help :: Text -> OptionDescriptionUpdate
help :: Text -> OptionDescription -> OptionDescription
help Text
t OptionDescription
o = OptionDescription
o {_help :: Maybe Text
_help = forall a. a -> Maybe a
Just Text
t}

-- | Display a 'OptionDescription' name
--   as a hyphenated name
--   return @<empty>@ if no name has been defined yet
displayCliOptionName :: OptionDescription -> Text
displayCliOptionName :: OptionDescription -> Text
displayCliOptionName OptionDescription
o =
  case OptionDescription -> [Text]
getNames OptionDescription
o of
    Text
n : [Text]
_ -> Text -> Text
camelCaseToHyphenated Text
n
    [] -> forall a. a -> Maybe a -> a
fromMaybe Text
"<empty>" (OptionDescription -> Maybe Text
_metavar OptionDescription
o)

-- | Return the possible names for an 'OptionDescription' if they are defined
getNames :: OptionDescription -> [Text]
getNames :: OptionDescription -> [Text]
getNames OptionDescription
o = forall a. [Maybe a] -> [a]
catMaybes [OptionDescription -> Maybe Text
_name OptionDescription
o, Char -> Text
T.singleton forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> OptionDescription -> Maybe Char
_shortName OptionDescription
o] forall a. Semigroup a => a -> a -> a
<> OptionDescription -> [Text]
_aliases OptionDescription
o