{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- | Railway-Oriented error handling for Haskell applications.
--
-- Monad.Rail is a Haskell library implementing Railway-Oriented Programming (ROP),
-- a functional approach to error handling that makes both success and failure paths
-- explicit and composable.
--
-- == What is Railway-Oriented Programming?
--
-- Railway-Oriented Programming uses a railway analogy: your program has two tracks,
-- one for success and one for failure. Operations can move between tracks, and once
-- on the failure track, you stay there until the end.
--
-- This library implements ROP through:
--
-- * 'RailT' - A monad transformer for railway computations
-- * 'throwError' - Moving to the failure track
-- * '(<!>)' - Combining validations while collecting all errors
-- * Error accumulation - Multiple errors are gathered, not just the first one
--
-- == Quick Start
--
-- Implement 'HasErrorInfo' with 'errorPublicMessage' — the only required method.
-- Derive 'Data.Data.Data' to get an automatic error code from the constructor name:
--
-- >>> {-# LANGUAGE DeriveDataTypeable #-}
-- >>>
-- >>> data UserError = NameEmpty | EmailInvalid
-- >>>   deriving (Show, Data)
-- >>>
-- >>> instance HasErrorInfo UserError where
-- >>>   errorPublicMessage NameEmpty    = "Name cannot be empty"
-- >>>   errorPublicMessage EmailInvalid = "Email format is invalid"
--
-- Override individual methods when you need custom codes or 'errorDetails':
--
-- >>> instance HasErrorInfo UserError where
-- >>>   errorPublicMessage NameEmpty    = "Name cannot be empty"
-- >>>   errorPublicMessage EmailInvalid = "Email format is invalid"
-- >>>
-- >>>   errorCode NameEmpty    = "UserNameEmpty"
-- >>>   errorCode EmailInvalid = "UserEmailInvalid"
--
-- Use in your railway:
--
-- >>> validateUser :: Rail ()
-- >>> validateUser = do
-- >>>   checkName <!> checkEmail
-- >>>   saveToDatabase
-- >>>   liftIO $ putStrLn "User created!"
--
-- Run and handle the result:
--
-- >>> main :: IO ()
-- >>> main = do
-- >>>   result <- runRail validateUser
-- >>>   case result of
-- >>>     Right () -> putStrLn "Success!"
-- >>>     Left errors -> print errors
--
-- == Error Accumulation
--
-- The '<!>' operator is key to ROP. It runs both validations regardless of failure:
--
-- >>> validate :: Rail ()
-- >>> validate = do
-- >>>   checkName <!> checkEmail <!> checkAge
-- >>>   -- If any checks fail, ALL errors are reported together
--
-- == Core Concepts
--
-- * 'Rail' - The monad for your computations
-- * 'Failure' - Contains accumulated errors
-- * 'SomeError' - Wrapper for any error type
-- * 'HasErrorInfo' - Typeclass for custom errors
-- * '<!>' - Error accumulation operator
--
-- == Logging and Monitoring
--
-- Each error carries two separate records:
--
-- * 'PublicErrorInfo' - Safe for end users: message, code, and details.
--   Assembled via 'publicErrorInfo'; serialized to JSON in API responses.
--   Null fields are omitted.
-- * 'InternalErrorInfo' - Sensitive diagnostics: severity, internal message,
--   exception, and call stack. Assembled via 'internalErrorInfo'; implements
--   'ToJSON' for structured log output but is never included in public API
--   responses. Null fields are omitted.
--
-- The 'Failure' type implements 'ToJSON', so errors serialize automatically:
--
-- >>> import Data.Aeson (encode)
-- >>> result <- runRail myRail
-- >>> case result of
-- >>>   Left errors -> BS.putStrLn $ encode errors
-- >>>   Right _ -> pure ()
module Monad.Rail
  ( -- * Core Types
    RailT (..),
    Rail,
    Failure (..),

    -- * Running Railways
    runRailT,
    runRail,

    -- * Throwing Errors
    throwError,
    throwUnhandledException,
    throwUnhandledExceptionWithCode,

    -- * Exception handling
    tryRail,
    tryRailWithCode,
    tryRailWithError,

    -- * Operators
    (<!>),

    -- * Error types
    ErrorSeverity (..),
    SomeErrorDetails (..),
    PublicErrorInfo (..),
    InternalErrorInfo (..),
    HasErrorInfo (..),
    publicErrorInfo,
    internalErrorInfo,
    SomeError (..),
    UnhandledException (..),
  )
where

import Monad.Rail.Error
import Monad.Rail.Types