Skip to main content

nwep/
role.rs

1use crate::ffi;
2use std::ffi::{CStr, CString};
3use std::fmt;
4
5/// `ServerRole` describes the functional role of an NWEP server node.
6///
7/// The role affects how the C library routes incoming requests internally:
8/// certain path prefixes are intercepted and handled by built-in subsystems
9/// rather than dispatched to application-level handlers. A server built with
10/// the Rust bindings must choose a role at construction time via
11/// `ServerBuilder::role`.
12///
13/// The `Default` implementation returns [`ServerRole::Regular`], which is the
14/// appropriate choice for application-level servers.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
16pub enum ServerRole {
17    /// `Regular` identifies a standard application server.
18    ///
19    /// All incoming requests are dispatched to the application's registered
20    /// handlers. No paths are intercepted by the library. This is the default
21    /// role and should be used for all nodes that do not serve trust-log or
22    /// anchor functions.
23    #[default]
24    Regular,
25
26    /// `LogServer` identifies a node that hosts the distributed Merkle
27    /// identity log.
28    ///
29    /// In addition to application handlers, a `LogServer` intercepts all
30    /// requests whose path begins with `/log/` and routes them to the C
31    /// library's built-in log management subsystem. These paths handle log
32    /// entry submission, Merkle proof retrieval, and checkpoint synchronization.
33    ///
34    /// Only designate a node as `LogServer` if it has been provisioned with
35    /// persistent log storage and configured as a trust authority in the
36    /// network's root anchoring document.
37    LogServer,
38
39    /// `Anchor` identifies a BLS threshold signing authority node.
40    ///
41    /// An `Anchor` intercepts all requests whose path begins with
42    /// `/checkpoint/` and routes them to the C library's built-in BLS
43    /// checkpoint subsystem. Anchor nodes hold a BLS private key share and
44    /// participate in threshold signing rounds to produce periodic checkpoints
45    /// over the Merkle log root.
46    ///
47    /// The network requires at least [`crate::types::DEFAULT_ANCHOR_THRESHOLD`]
48    /// functioning anchor nodes to ratify a checkpoint. Anchor nodes are
49    /// registered in the trust root and their BLS public keys are well-known.
50    Anchor,
51}
52
53impl ServerRole {
54    /// `from_str` parses a role string (as advertised in the `roles` header)
55    /// and returns the corresponding [`ServerRole`] variant.
56    ///
57    /// The parsing is delegated to the C library. Unrecognized strings map to
58    /// [`ServerRole::Regular`], which is the safe default.
59    ///
60    /// # Example
61    ///
62    /// ```rust
63    /// use nwep::role::ServerRole;
64    ///
65    /// assert_eq!(ServerRole::from_str("regular"),    ServerRole::Regular);
66    /// assert_eq!(ServerRole::from_str("log_server"), ServerRole::LogServer);
67    /// assert_eq!(ServerRole::from_str("anchor"),     ServerRole::Anchor);
68    /// assert_eq!(ServerRole::from_str("unknown"),    ServerRole::Regular);
69    /// ```
70    pub fn from_str(s: &str) -> Self {
71        let c = CString::new(s).unwrap_or_default();
72        let r = unsafe { ffi::nwep_role_from_str(c.as_ptr()) };
73        match r {
74            ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER => ServerRole::LogServer,
75            ffi::nwep_server_role_NWEP_ROLE_ANCHOR => ServerRole::Anchor,
76            _ => ServerRole::Regular,
77        }
78    }
79
80    /// `as_str` returns the canonical lowercase wire-format string for this
81    /// role.
82    ///
83    /// The string is retrieved from the C library's string table and is valid
84    /// for the lifetime of the process. It is the same value that appears in
85    /// the `roles` header of a `connect` response.
86    ///
87    /// # Example
88    ///
89    /// ```rust
90    /// use nwep::role::ServerRole;
91    ///
92    /// assert_eq!(ServerRole::Regular.as_str(),   "regular");
93    /// assert_eq!(ServerRole::LogServer.as_str(), "log_server");
94    /// assert_eq!(ServerRole::Anchor.as_str(),    "anchor");
95    /// ```
96    pub fn as_str(&self) -> &'static str {
97        let r = match self {
98            ServerRole::Regular => ffi::nwep_server_role_NWEP_ROLE_REGULAR,
99            ServerRole::LogServer => ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER,
100            ServerRole::Anchor => ffi::nwep_server_role_NWEP_ROLE_ANCHOR,
101        };
102        unsafe {
103            let ptr = ffi::nwep_role_to_str(r);
104            if ptr.is_null() {
105                "regular"
106            } else {
107                CStr::from_ptr(ptr).to_str().unwrap_or("regular")
108            }
109        }
110    }
111
112    /// `to_ffi` converts this [`ServerRole`] to the raw `nwep_server_role`
113    /// integer discriminant expected by C library functions.
114    #[allow(dead_code)]
115    pub(crate) fn to_ffi(&self) -> ffi::nwep_server_role {
116        match self {
117            ServerRole::Regular => ffi::nwep_server_role_NWEP_ROLE_REGULAR,
118            ServerRole::LogServer => ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER,
119            ServerRole::Anchor => ffi::nwep_server_role_NWEP_ROLE_ANCHOR,
120        }
121    }
122}
123
124impl fmt::Display for ServerRole {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        f.write_str(self.as_str())
127    }
128}
129
130impl From<ffi::nwep_server_role> for ServerRole {
131    fn from(r: ffi::nwep_server_role) -> Self {
132        match r {
133            ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER => ServerRole::LogServer,
134            ffi::nwep_server_role_NWEP_ROLE_ANCHOR => ServerRole::Anchor,
135            _ => ServerRole::Regular,
136        }
137    }
138}