Skip to main content

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}