///|
/// Behaviour when an Include pattern has no matching files.
pub(all) enum MissingIncludePolicy {
  Ignore
  Warn
  Error
} derive(Debug, Eq)

///|
/// Resource and compatibility limits for a single load operation.
///
/// Every limit is checked before the corresponding counter is incremented.
pub(all) struct LoadOptions {
  max_depth : Int
  max_files : Int
  max_file_bytes : Int
  max_total_bytes : Int
  max_glob_matches : Int
  max_include_arguments : Int
  missing_include : MissingIncludePolicy
  relative_include_base : String?
} derive(Debug, Eq)

///|
/// The conservative defaults used by `load` when no options are supplied.
pub fn LoadOptions::default() -> LoadOptions {
  {
    max_depth: 32,
    max_files: 1024,
    max_file_bytes: 8 * 1024 * 1024,
    max_total_bytes: 64 * 1024 * 1024,
    max_glob_matches: 4096,
    max_include_arguments: 256,
    missing_include: Warn,
    relative_include_base: None,
  }
}

///|
/// Provenance for one physical configuration source read during loading.
pub(all) struct SourceFile {
  identity : String
  display_path : String
  included_from : @syntax.SourceLocation?
} derive(Debug, Eq)

///|
/// A non-fatal event retained for callers that need diagnostics without
/// changing the configured missing-Include policy.
pub(all) enum LoadDiagnostic {
  MissingInclude(path~ : String, included_from~ : @syntax.SourceLocation?)
} derive(Debug, Eq)

///|
/// A parsed configuration together with all source provenance and diagnostics.
///
/// `config` is parsed with the supplied display path and has every Include
/// recursively expanded in source order.
pub(all) struct LoadedConfig {
  config : @syntax.Config
  sources : Array[SourceFile]
  diagnostics : Array[LoadDiagnostic]
} derive(Debug, Eq)

///|
/// Stable categories for a context-rich loading failure.
///
/// The legacy `LoadError` constructors remain available for source
/// compatibility. New loader code raises `LoadError::Detailed` so every
/// operational failure can retain the attempted path, Include location, and
/// complete display-path chain.
pub(all) enum LoadFailureKind {
  ReadFailed
  GlobFailed
  IncludeCycle
  IncludeDepthExceeded(limit~ : Int)
  FileLimitExceeded(limit~ : Int)
  FileTooLarge(limit~ : Int)
  TotalBytesExceeded(limit~ : Int)
  GlobLimitExceeded(limit~ : Int)
  IncludeArgumentLimitExceeded(limit~ : Int)
  InvalidInclude
  ParseFailed
  InvalidOptions
} derive(Debug, Eq)

///|
/// Context retained for every failure raised by `load`.
///
/// `chain` uses display paths, begins at the root, and includes
/// `attempted_path` for file-loading failures. For a glob or malformed Include
/// argument, the attempted value is kept separately while `chain` identifies
/// the active file stack.
pub(all) struct LoadFailure {
  kind : LoadFailureKind
  attempted_path : String
  location : @syntax.SourceLocation?
  include_location : @syntax.SourceLocation?
  chain : Array[String]
  message : String
} derive(Debug, Eq)

///|
/// Loading failures, including resource-limit failures which are never
/// converted into non-fatal missing-Include diagnostics.
///
/// The original constructors are retained for compatibility with callers that
/// construct or classify legacy values. `load` itself raises `Detailed`.
pub(all) suberror LoadError {
  ReadFailed(path~ : String, message~ : String)
  IncludeCycle(chain~ : Array[String])
  IncludeDepthExceeded(limit~ : Int, chain~ : Array[String])
  FileLimitExceeded(limit~ : Int)
  FileTooLarge(path~ : String, limit~ : Int)
  TotalBytesExceeded(limit~ : Int)
  GlobLimitExceeded(path~ : String, limit~ : Int)
  IncludeArgumentLimitExceeded(limit~ : Int, location~ : @syntax.SourceLocation)
  InvalidInclude(location~ : @syntax.SourceLocation, message~ : String)
  ParseFailed(location~ : @syntax.SourceLocation, message~ : String)
  InvalidOptions(message~ : String)
  Detailed(failure~ : LoadFailure)
} derive(Debug, Eq)