1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2026 Noyalib. All rights reserved.
//! Pluggable error-message formatters for user-facing rendering.
//!
//! [`crate::Error`]'s `Display` impl is developer-facing — it
//! preserves the noyalib-internal vocabulary (`!!binary`, "merge
//! key", "recursion depth limit") that's useful for debugging but
//! noisy for end-users of a config-loading binary. The
//! [`MessageFormatter`] trait lets callers plug in their own
//! formatting strategy: localisation tables, simplification,
//! richer formatting, or anything else.
//!
//! Two implementations ship in-tree:
//!
//! * [`DefaultFormatter`] — preserves the standard developer-facing
//! message verbatim. Equivalent to `format!("{err}")`.
//! * [`UserFormatter`] — collapses noyalib's diagnostic vocabulary
//! into short user-friendly sentences ("The configuration file
//! has a syntax error on line 5.") suitable for surfacing in
//! GUIs and `--help`-style command output.
//!
//! Use [`crate::Error::render_with_formatter`] to render an error
//! through a chosen formatter.
//!
//! # Examples
//!
//! ```
//! use noyalib::i18n::{DefaultFormatter, UserFormatter};
//! use noyalib::{from_str, Value};
//!
//! let err = from_str::<Value>("a: [unclosed").unwrap_err();
//! let dev = err.render_with_formatter(&DefaultFormatter);
//! let user = err.render_with_formatter(&UserFormatter);
//! assert!(!dev.is_empty());
//! assert!(!user.is_empty());
//! ```
use crateError;
use crate*;
/// Pluggable formatter for converting an [`Error`] into a
/// user-visible message.
///
/// Implement this trait to plug in localisation, simplification,
/// or rich formatting strategies. The trait is `Send + Sync` so a
/// single formatter instance can be shared across threads.
/// Default formatter — preserves the standard developer-facing
/// message verbatim.
///
/// Equivalent to `format!("{err}")`. The reference implementation
/// `MessageFormatter` consumers should compare against.
///
/// # Examples
///
/// ```
/// use noyalib::i18n::{DefaultFormatter, MessageFormatter};
/// use noyalib::{from_str, Value};
///
/// let err = from_str::<Value>("a: [unclosed").unwrap_err();
/// let s = DefaultFormatter.format(&err);
/// assert_eq!(s, err.to_string());
/// ```
;
/// User-facing formatter — collapses noyalib's diagnostic
/// vocabulary into short, plain-language sentences appropriate
/// for non-developer audiences (CLI `--help` text, GUI alert
/// dialogs).
///
/// Maps the major [`Error`] variants onto user-readable
/// templates:
///
/// | Variant family | User message |
/// | :--- | :--- |
/// | `Parse`, `ParseWithLocation` | `"The configuration file has a syntax error at line N."` |
/// | `Deserialize`, `DeserializeWithLocation` | `"The configuration file does not match the expected shape."` |
/// | `Io` | `"Could not read the configuration file."` |
/// | `RecursionLimitExceeded`, `Budget`, `RepetitionLimitExceeded` | `"The configuration file is too large or deeply nested."` |
/// | `DuplicateKey` | `"A configuration key appears twice."` |
/// | `UnknownAnchor`, `UnknownAnchorAt` | `"A configuration reference points at something that does not exist."` |
/// | `MissingField` | `"A required configuration field is missing."` |
/// | `TypeMismatch` | `"A configuration value has the wrong type."` |
/// | other | `"The configuration file is invalid."` |
///
/// Line numbers are included when the source location is
/// available; sensitive field names and noyalib internal terms
/// (`!!binary`, "merge key") are stripped.
///
/// # Examples
///
/// ```
/// use noyalib::i18n::{MessageFormatter, UserFormatter};
/// use noyalib::{from_str, Value};
///
/// let err = from_str::<Value>("a: [unclosed").unwrap_err();
/// let msg = UserFormatter.format(&err);
/// assert!(msg.contains("syntax error"));
/// ```
;