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
171
172
173
174
//! Error taxonomy for the RSS provider, plus the one length bound every
//! feed-influenced diagnostic string in this provider is held to.
use Error;
/// Length cap, in characters, on any feed-influenced diagnostic string this
/// provider stores or logs.
///
/// Its call sites share it because they are bounded by the same thing — how
/// many characters of feed-chosen text a document can push into a diagnostic —
/// rather than by coincidence:
///
/// - `feeds.last_error` (`engine.rs`, the column's only writer), including the
/// fixed literal `cache.rs` writes for an evicted-window `304`;
/// - `feeds.dialect_declared`'s `unknown:<root element>` form
/// (`conformance.rs`), built from a raw root element name of whatever length
/// the document supplies;
/// - `feeds.conformance_notes`, both per-note at the source
/// (`conformance.rs`) and over the joined string;
/// - `feeds.site_url`, a feed-authored link — not a diagnostic, but an
/// identifier rather than prose, so this is the bound that fits it and not
/// [`MAX_FEED_TEXT_CHARS`];
/// - the `debug`-level parse-failure line (`parse.rs`), which logs the
/// dependency's own reason and would otherwise be bounded only by
/// `max_response_bytes`.
///
/// The columns among those are held to it structurally by
/// `FeedObservation::capped` (`cache.rs`), the one boundary every observation
/// the cache retains passes through. Named in code font, not linked: this
/// module compiles without the `rss` feature while `cache` exists only behind
/// it, so an intra-doc link here is broken in exactly the builds this module
/// stays parseable for. The two sites that land in later phases of this stack
/// (`engine.rs`, `parse.rs`) are not present yet.
///
/// A bound on *length* only. What content may reach `feeds.last_error` at all is
/// a separate question, to be argued in `engine.rs`'s module doc when it lands.
///
/// The RSS docs (`docs/rss.md` and `docs/rss/semantics.yaml`, published later
/// in this stack) will spell the number as a bare `512`; neither is Rust and
/// neither can reference this constant, so both name it as `MAX_ERROR_CHARS`'s
/// value and this is where it is defined.
pub const MAX_ERROR_CHARS: usize = 512;
/// Length cap, in characters, on the feed-authored *prose* fields —
/// `feeds.title` and `feeds.description` — that enter a `FeedObservation`
/// (`cache.rs`; code font rather than a link for the reason given on
/// [`MAX_ERROR_CHARS`]).
///
/// Deliberately looser than [`MAX_ERROR_CHARS`]. Those two columns are not
/// diagnostics: they carry the feed's own editorial text, where a channel
/// description running past 512 characters is ordinary rather than hostile,
/// and cutting there would truncate legitimate values. They still need *a*
/// bound, and it is the same invariant [`MAX_ERROR_CHARS`] serves — the
/// observation store is never byte-bounded by the cache, whose budget meters
/// `RecordBatch` bytes only (`MemoryFeedCache::record_success`), so a
/// `max_response_bytes`-sized `<title>` would otherwise be retained whole and
/// outlive the window it described.
pub const MAX_FEED_TEXT_CHARS: usize = 4096;
/// Bound a string to a character count, cutting on a char boundary — the
/// crate-shared [`truncate_chars`](crate::util::text::truncate_chars),
/// re-exported under this provider's historical name so its call sites (and
/// their pairing with [`MAX_ERROR_CHARS`] / [`MAX_FEED_TEXT_CHARS`] above)
/// read as before.
pub use cratetruncate_chars as truncate;
/// Errors surfaced while validating or registering an RSS/Atom data source.
///
/// [`RssError::InvalidConfig`] is the only variant [`super::config::RssConfig::validate`]
/// can return today, and it runs with zero I/O — no network, no file reads —
/// so a misconfigured source fails at config-load time with a targeted
/// message rather than an opaque failure at first query. The remaining
/// variants belong to the registration path, which does know which data
/// source it is registering and fills `name` with it.