//! Friendly error layer and i18n message catalog (ADR-0019).
//!
//! Single chokepoint for user-visible message text. Engine
//! errors flow through `translate()` into a structured
//! [`FriendlyError`] payload that the renderer (in
//! `output_render`) composes into final output. Every other
//! user-visible string in the codebase migrates to this
//! catalog over time via the [`t!`] macro (ADR-0019 §9).
//!
//! ## Catalog
//!
//! The catalog lives in `strings/<locale>.yaml`, embedded at
//! compile time and parsed once on first access. Today the only
//! locale is `en-US`; runtime selection is deferred (ADR-0019
//! §8.2). Hierarchical YAML keys flatten to dot-paths
//! internally — `error.unique.insert.verbose` is the path the
//! `t!()` macro and the translator look up.
//!
//! ## The `t!()` macro
//!
//! ```ignore
//! use rdbms_playground::t;
//! let s = t!("error.unique.insert.verbose",
//!            table = "Customers", column = "id");
//! ```
//!
//! Placeholder values implement `Display`. Format specifiers
//! (`{name:08.2}`, `{name:>10}`, …) are explicitly rejected at
//! substitution time — see ADR-0019 §8.4.

pub mod error;
pub mod format;
pub mod keys;
pub mod translate;

pub use error::{DiagnosticTable, FriendlyError};
pub use format::{Catalog, catalog};
pub use translate::{FailureContext, Operation, TranslateContext, Verbosity, error_hint_class};

// `translate::translate` and `format::translate` are different
// callables — the former is the structured DbError → FriendlyError
// classifier (the H1 entry point); the latter is the lower-level
// catalog lookup the `t!()` macro expands to. Re-export both
// under non-conflicting names.
pub use format::translate;
pub use translate::translate as translate_error;

/// Look up `key` in the catalog and substitute named arguments.
///
/// Panics if the key is missing, if the template has malformed
/// placeholders, or if the args don't supply every name the
/// template references. The catalog validator unit test (Step 7,
/// ADR-0019 §8.6) catches these at build time so they should
/// never fire at runtime.
#[macro_export]
macro_rules! t {
    ($key:literal $(, $name:ident = $value:expr)* $(,)?) => {{
        $crate::friendly::translate(
            $key,
            &[$( (stringify!($name), &$value as &dyn ::std::fmt::Display) ),*],
        )
    }};
}
