content: module HsBindgen.Backend.UniqueId
    ( UniqueId (..)
    , UniqueIdMsg (..)
    , checkUniqueId
    ) where
  
  import Text.SimplePrettyPrint qualified as PP
  
  import HsBindgen.Imports
  import HsBindgen.Util.Tracer
  
  -- | C uses a global namespace. We must ensure that identifiers generated by
  -- @hs-bindgen@ are unique.
  --
  -- Defaults to the empty string @'UniqueId' ""@, which is __suboptimal__. We
  -- encourage using unique identifiers such as
  -- "com.well-typed.your-package-name".
  data UniqueId = UniqueId { unUniqueId :: String }
    deriving stock (Show, Eq, Ord)
  
  instance Default UniqueId where
    def = UniqueId ""
  
  -- | Maximum length of 'UniqueId' conforming to the C standard.
  --
  -- The C standard guarantees:
  -- - at least 63 significant characters for internal ids,
  -- - at least 31 significant characters for external ids;
  -- - but only for historical reasons, and all new implementations support at
  --   least 255 significant characters for external ids.
  --
  -- Calculation of maximum length:
  --
  -- - The @hs_bindgen_@ prefix has 11 characters.
  --   > hs_bindgen_
  --   > 12345678901
  -- - The hash suffix has 1 + 16 characters.
  --   > _HashHas16Characs
  --   > 12345678901234567
  --
  -- That is, the unique ID can have a maximum of 63 - 11 - 17 = 35 characters.
  maxUniqueIdLength :: Int
  maxUniqueIdLength = 35
  
  data UniqueIdMsg =
      UniqueIdEmpty
    | UniqueIdTooLong UniqueId
    deriving (Show, Eq, Ord)
  
  instance PrettyForTrace UniqueIdMsg where
    prettyForTrace = \case
      UniqueIdEmpty -> PP.vcat [
          "empty unique identifier ('UniqueId'):"
        , "  C uses a global namespace."
        , "  We encourage using a unique identifier to avoid duplicate symbol names."
        , "  For example, use and adapt 'com.example.package'."
        ]
      UniqueIdTooLong (UniqueId val) -> PP.vcat $ map PP.string [
          "unique identifier too long: " <> val
        , "  The maximum unique identifier length is " <> show maxUniqueIdLength <> "."
        , "  Your unique identifier has " <> show (length val) <> " characters."
        , "  Reason: The C standard only guarantees 63 significant initial characters."
        ]
  
  instance IsTrace Level UniqueIdMsg where
    getDefaultLogLevel = \case
      UniqueIdEmpty     -> Warning
      UniqueIdTooLong _ -> Notice
    getSource  = const HsBindgen
    getTraceId = const "unique-id"
  
  checkUniqueId :: Tracer IO UniqueIdMsg -> UniqueId -> IO ()
  checkUniqueId tracer uniqueId@(UniqueId val) = do
    when (null val) $
      traceWith tracer UniqueIdEmpty
    when (length val > maxUniqueIdLength) $
      traceWith tracer (UniqueIdTooLong uniqueId)
  