actrpc-interceptor 0.1.0

Concrete interceptors for ActRPC.
Documentation
use crate::interceptors::policy::config::PolicyFactPath;
use actrpc_core::{
    interception::{InterceptionPhase, InterceptionRequest},
    json_rpc::{JsonRpcMessage, JsonRpcParams, JsonRpcResponse, JsonRpcSingleMessage},
};

pub struct PolicyFacts<'a> {
    request: &'a InterceptionRequest,
}

impl<'a> PolicyFacts<'a> {
    pub fn new(request: &'a InterceptionRequest) -> Self {
        Self { request }
    }

    pub fn get_string(&self, path: &PolicyFactPath) -> Option<String> {
        let path = path.as_str();

        match path {
            "phase" => return self.phase(),
            "origin.kind" => return Some(self.request.origin.kind.to_string()),
            "origin.id" => return Some(self.request.origin.id.clone()),
            "message.method" => return self.message_method(),
            "message.error.code" => return self.message_error_code(),
            "message.error.message" => return self.message_error_message(),
            _ => {}
        }

        if let Some(rest) = path.strip_prefix("message.params.") {
            return self.message_params_path(rest);
        }

        if let Some(rest) = path.strip_prefix("message.result.") {
            return self.message_result_path(rest);
        }

        if let Some(rest) = path.strip_prefix("message.error.data.") {
            return self.message_error_data_path(rest);
        }

        None
    }

    fn phase(&self) -> Option<String> {
        match self.request.phase().ok()? {
            InterceptionPhase::Outbound => Some("outbound".to_owned()),
            InterceptionPhase::Inbound => Some("inbound".to_owned()),
        }
    }

    fn message_method(&self) -> Option<String> {
        match &self.request.message {
            JsonRpcMessage::Single(JsonRpcSingleMessage::Request(request)) => {
                Some(request.method.clone())
            }

            JsonRpcMessage::Single(JsonRpcSingleMessage::Notification(notification)) => {
                Some(notification.method.clone())
            }

            _ => None,
        }
    }

    fn message_params_path(&self, path: &str) -> Option<String> {
        let value = match &self.request.message {
            JsonRpcMessage::Single(JsonRpcSingleMessage::Request(request)) => {
                params_to_value(request.params.as_ref()?)
            }

            JsonRpcMessage::Single(JsonRpcSingleMessage::Notification(notification)) => {
                params_to_value(notification.params.as_ref()?)
            }

            _ => return None,
        };

        json_path_to_string(&value, path)
    }

    fn message_result_path(&self, path: &str) -> Option<String> {
        let value = match &self.request.message {
            JsonRpcMessage::Single(JsonRpcSingleMessage::Response(JsonRpcResponse::Success(
                success,
            ))) => &success.result,

            _ => return None,
        };

        json_path_to_string(value, path)
    }

    fn message_error_code(&self) -> Option<String> {
        match &self.request.message {
            JsonRpcMessage::Single(JsonRpcSingleMessage::Response(JsonRpcResponse::Error(
                error,
            ))) => Some(error.error.code.to_string()),

            _ => None,
        }
    }

    fn message_error_message(&self) -> Option<String> {
        match &self.request.message {
            JsonRpcMessage::Single(JsonRpcSingleMessage::Response(JsonRpcResponse::Error(
                error,
            ))) => Some(error.error.message.clone()),

            _ => None,
        }
    }

    fn message_error_data_path(&self, path: &str) -> Option<String> {
        let value = match &self.request.message {
            JsonRpcMessage::Single(JsonRpcSingleMessage::Response(JsonRpcResponse::Error(
                error,
            ))) => error.error.data.as_ref()?,

            _ => return None,
        };

        json_path_to_string(value, path)
    }
}

fn params_to_value(params: &JsonRpcParams) -> serde_json::Value {
    match params {
        JsonRpcParams::Array(values) => serde_json::Value::Array(values.clone()),
        JsonRpcParams::Object(map) => serde_json::Value::Object(map.clone()),
    }
}

fn json_path_to_string(value: &serde_json::Value, path: &str) -> Option<String> {
    let mut current = value;

    for segment in path.split('.') {
        if segment.is_empty() {
            return None;
        }

        current = current.get(segment)?;
    }

    json_value_to_string(current)
}

fn json_value_to_string(value: &serde_json::Value) -> Option<String> {
    match value {
        serde_json::Value::String(value) => Some(value.clone()),
        serde_json::Value::Number(value) => Some(value.to_string()),
        serde_json::Value::Bool(value) => Some(value.to_string()),
        serde_json::Value::Null => Some("null".to_owned()),
        serde_json::Value::Array(_) | serde_json::Value::Object(_) => Some(value.to_string()),
    }
}