1use crate::tier::Tier;
16
17#[non_exhaustive]
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum Method {
24 GetContent,
27 GetCapsule,
29 GetModule,
31 GetManifest,
33 GetMetadata,
35 ListCapsules,
37 GetProof,
39 GetProofStatus,
41 GetAnchoredRoot,
44 GetCollection,
47 ListCollectionItems,
50 Health,
52 Methods,
54
55 GetNetworkInfo,
58 GetPeers,
60 Announce,
62 GetAvailability,
64 ListInventory,
66 FetchRange,
68
69 Stage,
72 CacheGetConfig,
74 CacheSetCapBytes,
76 CacheClear,
78 CacheListCached,
80 CacheRemoveCached,
82 CacheFetchAndCache,
84 ControlPeerStatus,
86 RpcDiscover,
88}
89
90impl Method {
91 pub const fn name(self) -> &'static str {
93 match self {
94 Method::GetContent => "dig.getContent",
95 Method::GetCapsule => "dig.getCapsule",
96 Method::GetModule => "dig.getModule",
97 Method::GetManifest => "dig.getManifest",
98 Method::GetMetadata => "dig.getMetadata",
99 Method::ListCapsules => "dig.listCapsules",
100 Method::GetProof => "dig.getProof",
101 Method::GetProofStatus => "dig.getProofStatus",
102 Method::GetAnchoredRoot => "dig.getAnchoredRoot",
103 Method::GetCollection => "dig.getCollection",
104 Method::ListCollectionItems => "dig.listCollectionItems",
105 Method::Health => "dig.health",
106 Method::Methods => "dig.methods",
107 Method::GetNetworkInfo => "dig.getNetworkInfo",
108 Method::GetPeers => "dig.getPeers",
109 Method::Announce => "dig.announce",
110 Method::GetAvailability => "dig.getAvailability",
111 Method::ListInventory => "dig.listInventory",
112 Method::FetchRange => "dig.fetchRange",
113 Method::Stage => "dig.stage",
114 Method::CacheGetConfig => "cache.getConfig",
115 Method::CacheSetCapBytes => "cache.setCapBytes",
116 Method::CacheClear => "cache.clear",
117 Method::CacheListCached => "cache.listCached",
118 Method::CacheRemoveCached => "cache.removeCached",
119 Method::CacheFetchAndCache => "cache.fetchAndCache",
120 Method::ControlPeerStatus => "control.peerStatus",
121 Method::RpcDiscover => "rpc.discover",
122 }
123 }
124
125 pub fn from_name(name: &str) -> Option<Method> {
128 Method::ALL.iter().copied().find(|m| m.name() == name)
129 }
130
131 pub const fn tier(self) -> Tier {
137 match self {
138 Method::GetContent
139 | Method::GetCapsule
140 | Method::GetModule
141 | Method::GetManifest
142 | Method::GetMetadata
143 | Method::ListCapsules
144 | Method::GetProof
145 | Method::GetProofStatus
146 | Method::GetAnchoredRoot
147 | Method::GetCollection
148 | Method::ListCollectionItems
149 | Method::Health
150 | Method::Methods => Tier::PublicRead,
151
152 Method::GetNetworkInfo
153 | Method::GetPeers
154 | Method::Announce
155 | Method::GetAvailability
156 | Method::ListInventory
157 | Method::FetchRange => Tier::Peer,
158
159 Method::Stage
160 | Method::CacheGetConfig
161 | Method::CacheSetCapBytes
162 | Method::CacheClear
163 | Method::CacheListCached
164 | Method::CacheRemoveCached
165 | Method::CacheFetchAndCache
166 | Method::ControlPeerStatus
167 | Method::RpcDiscover => Tier::Control,
168 }
169 }
170
171 pub const fn is_peer_reachable(self) -> bool {
180 matches!(
181 self,
182 Method::GetContent
183 | Method::GetNetworkInfo
184 | Method::GetPeers
185 | Method::Announce
186 | Method::GetAvailability
187 | Method::ListInventory
188 | Method::FetchRange
189 | Method::GetAnchoredRoot
190 | Method::GetCollection
191 | Method::ListCollectionItems
192 )
193 }
194
195 pub const fn summary(self) -> &'static str {
197 match self {
198 Method::GetContent => "Read a verified window of a resource's ciphertext.",
199 Method::GetCapsule => "Fetch the whole .dig module for (store, root).",
200 Method::GetModule => "Alias of dig.getCapsule.",
201 Method::GetManifest => "Fetch the public discovery manifest resource.",
202 Method::GetMetadata => "Fetch the plaintext metadata manifest.",
203 Method::ListCapsules => "List a store's confirmed capsules.",
204 Method::GetProof => "Get the real inclusion proof + execution-proof status.",
205 Method::GetProofStatus => "Poll a real execution-proof job by id.",
206 Method::GetAnchoredRoot => "Resolve a store's chain-anchored tip root.",
207 Method::GetCollection => "Get collection-level facts for NFT launcher ids.",
208 Method::ListCollectionItems => "List resolved NFT collection items (paginated).",
209 Method::Health => "Liveness and capability summary.",
210 Method::Methods => "List the method names this node implements.",
211 Method::GetNetworkInfo => "This node's peer-network posture.",
212 Method::GetPeers => "The peers this node currently knows.",
213 Method::Announce => "Accept a peer announcement (peer_id + addresses).",
214 Method::GetAvailability => "Batch-check whether this node holds items.",
215 Method::ListInventory => "Enumerate the stores / roots this node serves.",
216 Method::FetchRange => "Fetch a single verified range frame of a resource.",
217 Method::Stage => "Compile a local folder into a capsule .dig in-process.",
218 Method::CacheGetConfig => "Get the local-cache configuration.",
219 Method::CacheSetCapBytes => "Set the local-cache size cap.",
220 Method::CacheClear => "Clear the local cache.",
221 Method::CacheListCached => "List the durable cached modules.",
222 Method::CacheRemoveCached => "Remove one cached capsule.",
223 Method::CacheFetchAndCache => "Fetch and cache one capsule.",
224 Method::ControlPeerStatus => "Snapshot the node's peer network.",
225 Method::RpcDiscover => "Return the OpenRPC self-describe document.",
226 }
227 }
228
229 pub const ALL: &'static [Method] = &[
231 Method::GetContent,
232 Method::GetCapsule,
233 Method::GetModule,
234 Method::GetManifest,
235 Method::GetMetadata,
236 Method::ListCapsules,
237 Method::GetProof,
238 Method::GetProofStatus,
239 Method::GetAnchoredRoot,
240 Method::GetCollection,
241 Method::ListCollectionItems,
242 Method::Health,
243 Method::Methods,
244 Method::GetNetworkInfo,
245 Method::GetPeers,
246 Method::Announce,
247 Method::GetAvailability,
248 Method::ListInventory,
249 Method::FetchRange,
250 Method::Stage,
251 Method::CacheGetConfig,
252 Method::CacheSetCapBytes,
253 Method::CacheClear,
254 Method::CacheListCached,
255 Method::CacheRemoveCached,
256 Method::CacheFetchAndCache,
257 Method::ControlPeerStatus,
258 Method::RpcDiscover,
259 ];
260
261 pub fn peer_reachable_names() -> Vec<&'static str> {
264 Method::ALL
265 .iter()
266 .copied()
267 .filter(|m| m.is_peer_reachable())
268 .map(Method::name)
269 .collect()
270 }
271}
272
273impl std::fmt::Display for Method {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 f.write_str(self.name())
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282 use std::collections::HashSet;
283
284 #[test]
287 fn names_round_trip() {
288 for m in Method::ALL {
289 assert_eq!(Method::from_name(m.name()), Some(*m), "{}", m.name());
290 }
291 assert!(Method::from_name("dig.nope").is_none());
292 }
293
294 #[test]
297 fn names_unique() {
298 let names: HashSet<&str> = Method::ALL.iter().map(|m| m.name()).collect();
299 assert_eq!(names.len(), Method::ALL.len());
300 }
301
302 #[test]
308 fn peer_allowlist_matches_canonical() {
309 let mut got = Method::peer_reachable_names();
310 got.sort_unstable();
311 let mut want = vec![
312 "dig.getContent",
313 "dig.getNetworkInfo",
314 "dig.getPeers",
315 "dig.announce",
316 "dig.getAvailability",
317 "dig.listInventory",
318 "dig.fetchRange",
319 "dig.getAnchoredRoot",
320 "dig.getCollection",
321 "dig.listCollectionItems",
322 ];
323 want.sort_unstable();
324 assert_eq!(got, want);
325
326 for m in [
328 Method::Stage,
329 Method::CacheGetConfig,
330 Method::CacheSetCapBytes,
331 Method::CacheClear,
332 Method::CacheListCached,
333 Method::CacheRemoveCached,
334 Method::CacheFetchAndCache,
335 Method::ControlPeerStatus,
336 Method::RpcDiscover,
337 ] {
338 assert!(
339 !m.is_peer_reachable(),
340 "{} must NOT be peer-reachable",
341 m.name()
342 );
343 }
344 }
345
346 #[test]
349 fn anchored_reads_public_but_peer_reachable() {
350 for m in [
351 Method::GetAnchoredRoot,
352 Method::GetCollection,
353 Method::ListCollectionItems,
354 ] {
355 assert_eq!(m.tier(), Tier::PublicRead);
356 assert!(m.is_peer_reachable());
357 }
358 }
359
360 #[test]
363 fn control_tier_never_peer_reachable() {
364 for m in Method::ALL {
365 if m.tier() == Tier::Control {
366 assert!(
367 !m.is_peer_reachable(),
368 "{} is Control but peer-reachable",
369 m.name()
370 );
371 }
372 }
373 }
374}