{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- |
-- Module      : Language.Github.Actions.Defaults
-- Description : Default settings for GitHub Actions workflows and jobs
-- Copyright   : (c) 2025 Bellroy Pty Ltd
-- License     : BSD-3-Clause
-- Maintainer  : Bellroy Tech Team <haskell@bellroy.com>
--
-- This module provides the 'Defaults' type for setting default values that apply
-- to all steps within a job or all jobs within a workflow.
--
-- Defaults allow you to specify common settings like shell type and working directory
-- that will be inherited by all steps unless explicitly overridden at the step level.
--
-- For more information about GitHub Actions defaults, see:
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defaults>
module Language.Github.Actions.Defaults
  ( Defaults (..),
    gen,
  )
where

import Data.Aeson (FromJSON, ToJSON, (.:), (.:?), (.=))
import qualified Data.Aeson as Aeson
import Data.Maybe (catMaybes)
import Data.Text (Text)
import GHC.Generics (Generic)
import Hedgehog (MonadGen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Language.Github.Actions.Shell (Shell)
import qualified Language.Github.Actions.Shell as Shell

-- | Default settings for steps within a job or jobs within a workflow.
--
-- Defaults provide a convenient way to set common configuration that applies to
-- both Jobs and Workflows without having to repeat the same settings everywhere.
--
--
-- Example usage:
--
-- @
-- import Language.Github.Actions.Defaults
-- import Language.Github.Actions.Shell
--
-- -- Set bash as default shell for all steps
-- bashDefaults :: Defaults
-- bashDefaults = Defaults
--  { runShell = Just (Bash Nothing)
--  , runWorkingDirectory = Nothing
--  }
--
-- -- Set working directory for all steps
-- workdirDefaults :: Defaults
-- workdirDefaults = Defaults
--  { runShell = Nothing
--  , runWorkingDirectory = Just "/src"
--  }
-- @
--
-- For more details, see: <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defaults>
data Defaults = Defaults
  { -- | Default shell for run commands
    Defaults -> Maybe Shell
runShell :: Maybe Shell,
    -- | Default working directory for run commands
    Defaults -> Maybe Text
runWorkingDirectory :: Maybe Text
  }
  deriving stock (Defaults -> Defaults -> Bool
(Defaults -> Defaults -> Bool)
-> (Defaults -> Defaults -> Bool) -> Eq Defaults
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Defaults -> Defaults -> Bool
== :: Defaults -> Defaults -> Bool
$c/= :: Defaults -> Defaults -> Bool
/= :: Defaults -> Defaults -> Bool
Eq, (forall x. Defaults -> Rep Defaults x)
-> (forall x. Rep Defaults x -> Defaults) -> Generic Defaults
forall x. Rep Defaults x -> Defaults
forall x. Defaults -> Rep Defaults x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Defaults -> Rep Defaults x
from :: forall x. Defaults -> Rep Defaults x
$cto :: forall x. Rep Defaults x -> Defaults
to :: forall x. Rep Defaults x -> Defaults
Generic, Eq Defaults
Eq Defaults =>
(Defaults -> Defaults -> Ordering)
-> (Defaults -> Defaults -> Bool)
-> (Defaults -> Defaults -> Bool)
-> (Defaults -> Defaults -> Bool)
-> (Defaults -> Defaults -> Bool)
-> (Defaults -> Defaults -> Defaults)
-> (Defaults -> Defaults -> Defaults)
-> Ord Defaults
Defaults -> Defaults -> Bool
Defaults -> Defaults -> Ordering
Defaults -> Defaults -> Defaults
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Defaults -> Defaults -> Ordering
compare :: Defaults -> Defaults -> Ordering
$c< :: Defaults -> Defaults -> Bool
< :: Defaults -> Defaults -> Bool
$c<= :: Defaults -> Defaults -> Bool
<= :: Defaults -> Defaults -> Bool
$c> :: Defaults -> Defaults -> Bool
> :: Defaults -> Defaults -> Bool
$c>= :: Defaults -> Defaults -> Bool
>= :: Defaults -> Defaults -> Bool
$cmax :: Defaults -> Defaults -> Defaults
max :: Defaults -> Defaults -> Defaults
$cmin :: Defaults -> Defaults -> Defaults
min :: Defaults -> Defaults -> Defaults
Ord, Int -> Defaults -> ShowS
[Defaults] -> ShowS
Defaults -> String
(Int -> Defaults -> ShowS)
-> (Defaults -> String) -> ([Defaults] -> ShowS) -> Show Defaults
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Defaults -> ShowS
showsPrec :: Int -> Defaults -> ShowS
$cshow :: Defaults -> String
show :: Defaults -> String
$cshowList :: [Defaults] -> ShowS
showList :: [Defaults] -> ShowS
Show)

instance FromJSON Defaults where
  parseJSON :: Value -> Parser Defaults
parseJSON = String -> (Object -> Parser Defaults) -> Value -> Parser Defaults
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Aeson.withObject String
"Defaults" ((Object -> Parser Defaults) -> Value -> Parser Defaults)
-> (Object -> Parser Defaults) -> Value -> Parser Defaults
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
    Object
run <- Object
o Object -> Key -> Parser Object
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"run"
    Maybe Shell
runShell <- Object
run Object -> Key -> Parser (Maybe Shell)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"shell"
    Maybe Text
runWorkingDirectory <- Object
run Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"working-directory"
    Defaults -> Parser Defaults
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Defaults {Maybe Text
Maybe Shell
runShell :: Maybe Shell
runWorkingDirectory :: Maybe Text
runShell :: Maybe Shell
runWorkingDirectory :: Maybe Text
..}

instance ToJSON Defaults where
  toJSON :: Defaults -> Value
toJSON Defaults {Maybe Text
Maybe Shell
runShell :: Defaults -> Maybe Shell
runWorkingDirectory :: Defaults -> Maybe Text
runShell :: Maybe Shell
runWorkingDirectory :: Maybe Text
..} =
    [Pair] -> Value
Aeson.object
      [ Key
"run"
          Key -> Value -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= [Pair] -> Value
Aeson.object
            ( [Maybe Pair] -> [Pair]
forall a. [Maybe a] -> [a]
catMaybes
                [ (Key
"shell" Key -> Shell -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.=) (Shell -> Pair) -> Maybe Shell -> Maybe Pair
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Shell
runShell,
                  (Key
"working-directory" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.=) (Text -> Pair) -> Maybe Text -> Maybe Pair
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
runWorkingDirectory
                ]
            )
      ]

gen :: (MonadGen m) => m Defaults
gen :: forall (m :: * -> *). MonadGen m => m Defaults
gen = do
  Maybe Shell
runShell <- m Shell -> m (Maybe Shell)
forall (m :: * -> *) a. MonadGen m => m a -> m (Maybe a)
Gen.maybe m Shell
forall (m :: * -> *). MonadGen m => m Shell
Shell.gen
  Maybe Text
runWorkingDirectory <- m Text -> m (Maybe Text)
forall (m :: * -> *) a. MonadGen m => m a -> m (Maybe a)
Gen.maybe (Range Int -> m Char -> m Text
forall (m :: * -> *). MonadGen m => Range Int -> m Char -> m Text
Gen.text (Int -> Int -> Range Int
forall a. Integral a => a -> a -> Range a
Range.linear Int
1 Int
5) m Char
forall (m :: * -> *). MonadGen m => m Char
Gen.alphaNum)
  Defaults -> m Defaults
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Defaults {Maybe Text
Maybe Shell
runShell :: Maybe Shell
runWorkingDirectory :: Maybe Text
runShell :: Maybe Shell
runWorkingDirectory :: Maybe Text
..}