kevy_scope/routing.rs
1//! `Routing` — the verdict for one `route(key, self_node_id)` call.
2//! The server-cement layer translates this into one of:
3//! - **Owned** → execute the write locally
4//! - **Misdirected** → reply `-MISDIRECTED writer is <host:port>`
5//! - **Unknown** → no scope matches; fall back to default behaviour
6//! (today: accept locally; v3.x may flip to reject, see the RFC).
7
8/// Result of an [`crate::OwnershipTable::route`] lookup. Borrows the
9/// writer/target ids out of the table — caller copies only when it
10/// needs to log or encode them.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Routing<'a> {
13 /// `self_node_id` is the declared writer (or active fallback) for
14 /// the matching scope. Execute the write locally.
15 Owned,
16 /// Another node owns this write. The cement layer encodes
17 /// `-MISDIRECTED writer is <writer>` to the wire so the client
18 /// can follow. The `target` field is the *node id* — the server
19 /// resolves that to `host:port` from its peer table at encode
20 /// time (kevy-scope intentionally doesn't carry peer addrs).
21 Misdirected {
22 /// Node id of the actual writer (or active fallback) for
23 /// this key's scope.
24 target: &'a str,
25 },
26 /// No scope matched. Default policy is "accept locally" — the
27 /// scope system is opt-in, so keys outside declared scopes
28 /// behave like the pre-Phase-3 keyspace.
29 Unknown,
30}
31
32impl Routing<'_> {
33 /// `true` when the current node owns the write.
34 #[must_use]
35 pub fn is_local_writer(&self) -> bool {
36 matches!(self, Routing::Owned)
37 }
38
39 /// `true` for the `-MISDIRECTED` branch.
40 #[must_use]
41 pub fn is_misdirected(&self) -> bool {
42 matches!(self, Routing::Misdirected { .. })
43 }
44
45 /// `Some(target)` for `Misdirected`, else `None`. Convenience for
46 /// the cement layer's RESP encoder.
47 #[must_use]
48 pub fn misdirected_target(&self) -> Option<&str> {
49 if let Routing::Misdirected { target } = self {
50 Some(target)
51 } else {
52 None
53 }
54 }
55}