packages feed

github-actions-0.1.1.0: src/Language/Github/Actions/Job/Needs.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}

-- |
-- Module      : Language.Github.Actions.Job.Needs
-- Description : Job dependency specification for GitHub Actions
-- Copyright   : (c) 2025 Bellroy Pty Ltd
-- License     : BSD-3-Clause
-- Maintainer  : Bellroy Tech Team <haskell@bellroy.com>
--
-- This module provides the 'JobNeeds' type for representing job dependencies
-- in GitHub Actions workflows. GitHub Actions allows both strings and
-- lists of strings for the 'needs' field.
--
-- Examples of valid 'needs' specifications:
-- * @needs: build@ - Single job specified as a string
-- * @needs: [build]@ - Single job specified as a list of strings
-- * @needs: [build, test]@ - Multiple job dependencies specified as a list of strings
--
-- For more information about GitHub Actions job dependencies, see:
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds>
module Language.Github.Actions.Job.Needs
  ( JobNeeds (..),
    gen,
  )
where

import Data.Aeson (FromJSON, ToJSON (..), Value (..))
import qualified Data.Aeson as Aeson
import Data.List.NonEmpty (NonEmpty)
import GHC.Generics (Generic)
import Hedgehog (MonadGen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Language.Github.Actions.Job.Id (JobId)
import qualified Language.Github.Actions.Job.Id as JobId

-- | Job dependency specification that preserves YAML representation.
--
-- GitHub Actions supports flexible job dependency specification:
--
-- * 'JobNeedsString' - Single job dependency as string like @needs: build@
-- * 'JobNeedsArray' - Multiple job dependencies as array like @needs: [build, test]@
--
-- Examples:
--
-- @
-- -- Single job dependency (string form)
-- stringDep :: JobNeeds
-- stringDep = JobNeedsString (JobId "build")
--
-- -- Multiple job dependencies (array form)
-- arrayDeps :: JobNeeds
-- arrayDeps = JobNeedsArray (JobId "build" :| [JobId "test", JobId "lint"])
-- @
--
-- The type preserves the original YAML format during round-trip serialization.
-- A string input will serialize back to a string, and an array input will
-- serialize back to an array, preventing information loss.
data JobNeeds
  = JobNeedsString JobId
  | JobNeedsArray (NonEmpty JobId)
  deriving stock (Eq, Generic, Ord, Show)

instance FromJSON JobNeeds where
  parseJSON v@(Array _) = JobNeedsArray <$> Aeson.parseJSON v
  parseJSON v = JobNeedsString <$> Aeson.parseJSON v

instance ToJSON JobNeeds where
  toJSON (JobNeedsString jobId) = toJSON jobId
  toJSON (JobNeedsArray jobIds) = toJSON jobIds

gen :: (MonadGen m) => m JobNeeds
gen =
  Gen.choice
    [ JobNeedsString <$> JobId.gen,
      JobNeedsArray <$> Gen.nonEmpty (Range.linear 1 5) JobId.gen
    ]