nwep-rs 0.1.8

Rust bindings for the NWEP (WEB/1) protocol library
Documentation
use crate::ffi;
use std::ffi::{CStr, CString};
use std::fmt;

/// `ServerRole` describes the functional role of an NWEP server node.
///
/// The role affects how the C library routes incoming requests internally:
/// certain path prefixes are intercepted and handled by built-in subsystems
/// rather than dispatched to application-level handlers. A server built with
/// the Rust bindings must choose a role at construction time via
/// `ServerBuilder::role`.
///
/// The `Default` implementation returns [`ServerRole::Regular`], which is the
/// appropriate choice for application-level servers.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum ServerRole {
    /// `Regular` identifies a standard application server.
    ///
    /// All incoming requests are dispatched to the application's registered
    /// handlers. No paths are intercepted by the library. This is the default
    /// role and should be used for all nodes that do not serve trust-log or
    /// anchor functions.
    #[default]
    Regular,

    /// `LogServer` identifies a node that hosts the distributed Merkle
    /// identity log.
    ///
    /// In addition to application handlers, a `LogServer` intercepts all
    /// requests whose path begins with `/log/` and routes them to the C
    /// library's built-in log management subsystem. These paths handle log
    /// entry submission, Merkle proof retrieval, and checkpoint synchronization.
    ///
    /// Only designate a node as `LogServer` if it has been provisioned with
    /// persistent log storage and configured as a trust authority in the
    /// network's root anchoring document.
    LogServer,

    /// `Anchor` identifies a BLS threshold signing authority node.
    ///
    /// An `Anchor` intercepts all requests whose path begins with
    /// `/checkpoint/` and routes them to the C library's built-in BLS
    /// checkpoint subsystem. Anchor nodes hold a BLS private key share and
    /// participate in threshold signing rounds to produce periodic checkpoints
    /// over the Merkle log root.
    ///
    /// The network requires at least [`crate::types::DEFAULT_ANCHOR_THRESHOLD`]
    /// functioning anchor nodes to ratify a checkpoint. Anchor nodes are
    /// registered in the trust root and their BLS public keys are well-known.
    Anchor,
}

impl ServerRole {
    /// `from_str` parses a role string (as advertised in the `roles` header)
    /// and returns the corresponding [`ServerRole`] variant.
    ///
    /// The parsing is delegated to the C library. Unrecognized strings map to
    /// [`ServerRole::Regular`], which is the safe default.
    ///
    /// # Example
    ///
    /// ```rust
    /// use nwep::role::ServerRole;
    ///
    /// assert_eq!(ServerRole::from_str("regular"),    ServerRole::Regular);
    /// assert_eq!(ServerRole::from_str("log_server"), ServerRole::LogServer);
    /// assert_eq!(ServerRole::from_str("anchor"),     ServerRole::Anchor);
    /// assert_eq!(ServerRole::from_str("unknown"),    ServerRole::Regular);
    /// ```
    pub fn from_str(s: &str) -> Self {
        let c = CString::new(s).unwrap_or_default();
        let r = unsafe { ffi::nwep_role_from_str(c.as_ptr()) };
        match r {
            ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER => ServerRole::LogServer,
            ffi::nwep_server_role_NWEP_ROLE_ANCHOR => ServerRole::Anchor,
            _ => ServerRole::Regular,
        }
    }

    /// `as_str` returns the canonical lowercase wire-format string for this
    /// role.
    ///
    /// The string is retrieved from the C library's string table and is valid
    /// for the lifetime of the process. It is the same value that appears in
    /// the `roles` header of a `connect` response.
    ///
    /// # Example
    ///
    /// ```rust
    /// use nwep::role::ServerRole;
    ///
    /// assert_eq!(ServerRole::Regular.as_str(),   "regular");
    /// assert_eq!(ServerRole::LogServer.as_str(), "log_server");
    /// assert_eq!(ServerRole::Anchor.as_str(),    "anchor");
    /// ```
    pub fn as_str(&self) -> &'static str {
        let r = match self {
            ServerRole::Regular => ffi::nwep_server_role_NWEP_ROLE_REGULAR,
            ServerRole::LogServer => ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER,
            ServerRole::Anchor => ffi::nwep_server_role_NWEP_ROLE_ANCHOR,
        };
        unsafe {
            let ptr = ffi::nwep_role_to_str(r);
            if ptr.is_null() {
                "regular"
            } else {
                CStr::from_ptr(ptr).to_str().unwrap_or("regular")
            }
        }
    }

    /// `to_ffi` converts this [`ServerRole`] to the raw `nwep_server_role`
    /// integer discriminant expected by C library functions.
    #[allow(dead_code)]
    pub(crate) fn to_ffi(&self) -> ffi::nwep_server_role {
        match self {
            ServerRole::Regular => ffi::nwep_server_role_NWEP_ROLE_REGULAR,
            ServerRole::LogServer => ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER,
            ServerRole::Anchor => ffi::nwep_server_role_NWEP_ROLE_ANCHOR,
        }
    }
}

impl fmt::Display for ServerRole {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl From<ffi::nwep_server_role> for ServerRole {
    fn from(r: ffi::nwep_server_role) -> Self {
        match r {
            ffi::nwep_server_role_NWEP_ROLE_LOG_SERVER => ServerRole::LogServer,
            ffi::nwep_server_role_NWEP_ROLE_ANCHOR => ServerRole::Anchor,
            _ => ServerRole::Regular,
        }
    }
}