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

-- |
-- Module      : Language.Github.Actions.Service.Id
-- Description : Service identifiers for GitHub Actions workflows
-- Copyright   : (c) 2025 Bellroy Pty Ltd
-- License     : BSD-3-Clause
-- Maintainer  : Bellroy Tech Team <haskell@bellroy.com>
--
-- This module provides the 'ServiceId' type for uniquely identifying service
-- containers within GitHub Actions jobs.
--
-- Service IDs are used to reference service containers from job steps via hostname.
-- They must be unique within a job and follow specific naming conventions.
--
-- For more information about GitHub Actions service containers, see:
-- <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idservices>
module Language.Github.Actions.Service.Id
  ( ServiceId (..),
    gen,
    render,
  )
where

import Data.Aeson (FromJSON, FromJSONKey, ToJSON (..), ToJSONKey)
import Data.Text (Text)
import GHC.Generics (Generic)
import Hedgehog (MonadGen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

-- | A unique identifier for a service container within a GitHub Actions job.
--
-- Service IDs are used to:
-- * Reference the service container from job steps using the service ID as hostname
-- * Configure service-specific settings like ports, environment variables, and health checks
-- * Identify services in workflow run logs and container networks
--
-- Service IDs must be unique within a job and should follow these conventions:
-- * Start with a letter or underscore
-- * Contain only alphanumeric characters, hyphens, and underscores
-- * Be descriptive of the service's purpose
--
-- Example usage:
--
-- @
-- import Language.Github.Actions.Service.Id
--
-- -- Database service IDs
-- postgresServiceId :: ServiceId
-- postgresServiceId = ServiceId "postgres"
--
-- redisServiceId :: ServiceId
-- redisServiceId = ServiceId "redis"
--
-- -- More descriptive service IDs
-- testDatabaseServiceId :: ServiceId
-- testDatabaseServiceId = ServiceId "test-database"
-- @
--
-- For more details, see: <https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idservices>
newtype ServiceId = ServiceId Text
  deriving stock (ServiceId -> ServiceId -> Bool
(ServiceId -> ServiceId -> Bool)
-> (ServiceId -> ServiceId -> Bool) -> Eq ServiceId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ServiceId -> ServiceId -> Bool
== :: ServiceId -> ServiceId -> Bool
$c/= :: ServiceId -> ServiceId -> Bool
/= :: ServiceId -> ServiceId -> Bool
Eq, (forall x. ServiceId -> Rep ServiceId x)
-> (forall x. Rep ServiceId x -> ServiceId) -> Generic ServiceId
forall x. Rep ServiceId x -> ServiceId
forall x. ServiceId -> Rep ServiceId x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. ServiceId -> Rep ServiceId x
from :: forall x. ServiceId -> Rep ServiceId x
$cto :: forall x. Rep ServiceId x -> ServiceId
to :: forall x. Rep ServiceId x -> ServiceId
Generic, Eq ServiceId
Eq ServiceId =>
(ServiceId -> ServiceId -> Ordering)
-> (ServiceId -> ServiceId -> Bool)
-> (ServiceId -> ServiceId -> Bool)
-> (ServiceId -> ServiceId -> Bool)
-> (ServiceId -> ServiceId -> Bool)
-> (ServiceId -> ServiceId -> ServiceId)
-> (ServiceId -> ServiceId -> ServiceId)
-> Ord ServiceId
ServiceId -> ServiceId -> Bool
ServiceId -> ServiceId -> Ordering
ServiceId -> ServiceId -> ServiceId
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 :: ServiceId -> ServiceId -> Ordering
compare :: ServiceId -> ServiceId -> Ordering
$c< :: ServiceId -> ServiceId -> Bool
< :: ServiceId -> ServiceId -> Bool
$c<= :: ServiceId -> ServiceId -> Bool
<= :: ServiceId -> ServiceId -> Bool
$c> :: ServiceId -> ServiceId -> Bool
> :: ServiceId -> ServiceId -> Bool
$c>= :: ServiceId -> ServiceId -> Bool
>= :: ServiceId -> ServiceId -> Bool
$cmax :: ServiceId -> ServiceId -> ServiceId
max :: ServiceId -> ServiceId -> ServiceId
$cmin :: ServiceId -> ServiceId -> ServiceId
min :: ServiceId -> ServiceId -> ServiceId
Ord, Int -> ServiceId -> ShowS
[ServiceId] -> ShowS
ServiceId -> String
(Int -> ServiceId -> ShowS)
-> (ServiceId -> String)
-> ([ServiceId] -> ShowS)
-> Show ServiceId
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ServiceId -> ShowS
showsPrec :: Int -> ServiceId -> ShowS
$cshow :: ServiceId -> String
show :: ServiceId -> String
$cshowList :: [ServiceId] -> ShowS
showList :: [ServiceId] -> ShowS
Show)
  deriving newtype (Maybe ServiceId
Value -> Parser [ServiceId]
Value -> Parser ServiceId
(Value -> Parser ServiceId)
-> (Value -> Parser [ServiceId])
-> Maybe ServiceId
-> FromJSON ServiceId
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser ServiceId
parseJSON :: Value -> Parser ServiceId
$cparseJSONList :: Value -> Parser [ServiceId]
parseJSONList :: Value -> Parser [ServiceId]
$comittedField :: Maybe ServiceId
omittedField :: Maybe ServiceId
FromJSON, FromJSONKeyFunction [ServiceId]
FromJSONKeyFunction ServiceId
FromJSONKeyFunction ServiceId
-> FromJSONKeyFunction [ServiceId] -> FromJSONKey ServiceId
forall a.
FromJSONKeyFunction a -> FromJSONKeyFunction [a] -> FromJSONKey a
$cfromJSONKey :: FromJSONKeyFunction ServiceId
fromJSONKey :: FromJSONKeyFunction ServiceId
$cfromJSONKeyList :: FromJSONKeyFunction [ServiceId]
fromJSONKeyList :: FromJSONKeyFunction [ServiceId]
FromJSONKey, [ServiceId] -> Value
[ServiceId] -> Encoding
ServiceId -> Bool
ServiceId -> Value
ServiceId -> Encoding
(ServiceId -> Value)
-> (ServiceId -> Encoding)
-> ([ServiceId] -> Value)
-> ([ServiceId] -> Encoding)
-> (ServiceId -> Bool)
-> ToJSON ServiceId
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: ServiceId -> Value
toJSON :: ServiceId -> Value
$ctoEncoding :: ServiceId -> Encoding
toEncoding :: ServiceId -> Encoding
$ctoJSONList :: [ServiceId] -> Value
toJSONList :: [ServiceId] -> Value
$ctoEncodingList :: [ServiceId] -> Encoding
toEncodingList :: [ServiceId] -> Encoding
$comitField :: ServiceId -> Bool
omitField :: ServiceId -> Bool
ToJSON, ToJSONKeyFunction [ServiceId]
ToJSONKeyFunction ServiceId
ToJSONKeyFunction ServiceId
-> ToJSONKeyFunction [ServiceId] -> ToJSONKey ServiceId
forall a.
ToJSONKeyFunction a -> ToJSONKeyFunction [a] -> ToJSONKey a
$ctoJSONKey :: ToJSONKeyFunction ServiceId
toJSONKey :: ToJSONKeyFunction ServiceId
$ctoJSONKeyList :: ToJSONKeyFunction [ServiceId]
toJSONKeyList :: ToJSONKeyFunction [ServiceId]
ToJSONKey)

gen :: (MonadGen m) => m ServiceId
gen :: forall (m :: * -> *). MonadGen m => m ServiceId
gen = Text -> ServiceId
ServiceId (Text -> ServiceId) -> m Text -> m ServiceId
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> 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

render :: ServiceId -> Text
render :: ServiceId -> Text
render (ServiceId Text
t) = Text
t