data-rfc5280-0.1.0.0: src/Data/Rfc5280/Internal.hs
{-# LANGUAGE OverloadedStrings #-}
{- |
Module : Data.Rfc5280.Internal
Copyright : (c) 2026 Tim Emiola
Maintainer : Tim Emiola <adetokunbo@emio.la>
SPDX-License-Identifier: BSD3
Internal helpers shared across X.509 extension modules. Not part of the
public API stability guarantee.
-}
module Data.Rfc5280.Internal
( -- * Rendering
RenderConfig (..)
-- * OID
, OID
, OIDError (..)
, mkOID
, oidBuilder
-- * Builder helpers
, intersperseCommas
, intersperseWith
)
where
import Data.ByteString.Builder (Builder, intDec)
import Data.Foldable (foldl')
import Data.List.NonEmpty (NonEmpty (..))
{- | Types that can be rendered to a configuration value as a 'Builder'.
Implemented by all extension types; used by 'Data.Rfc5280.renderConfig'.
-}
class RenderConfig a where
-- | Render a value as a 'Builder' in OpenSSL extension configuration format.
renderBuilder :: a -> Builder
-- | An ASN.1 object identifier. Construct with 'mkOID'.
type OID = NonEmpty Int
-- | Failure modes for 'mkOID'.
data OIDError
= -- | First arc is not 0, 1, or 2.
InvalidFirstArc
| -- | One or more subsequent arcs are negative.
NegativeArc
deriving (Eq, Show)
{- | Construct an 'OID', validating that the first arc is 0–2 and all arcs
are non-negative.
The second-arc ≤ 39 constraint from X.660 is not enforced; it only applies
under first arcs 0 and 1 and is rarely violated in practice.
-}
mkOID :: Int -> [Int] -> Either OIDError OID
mkOID first rest
| first < 0 || first > 2 = Left InvalidFirstArc
| any (< 0) rest = Left NegativeArc
| otherwise = Right (first :| rest)
-- | Render an 'OID' as a dot-separated sequence of integers.
oidBuilder :: OID -> Builder
oidBuilder = intersperseWith "." . fmap intDec
-- | Render a non-empty list of 'Builder' values separated by commas.
intersperseCommas :: NonEmpty Builder -> Builder
intersperseCommas = intersperseWith ","
-- | Render a non-empty list of 'Builder' values separated by @sep@.
intersperseWith :: Builder -> NonEmpty Builder -> Builder
intersperseWith sep (x :| xs) = x <> foldl' (\acc y -> acc <> sep <> y) "" xs