saddle-core 0.3.37

Shared contracts for Saddle components
Documentation
//! Schema 1 projection. Legacy diagnostic serialization remains independent.
use super::*;
mod select;
pub use select::SelectedContextValue;
use crate::context_json::JsonSubtree;
use serde::ser::SerializeMap;

pub struct UnifiedContext<'a>(pub(super) &'a RequestExecutionView);

struct StringId(u64);
impl Serialize for StringId {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { s.collect_str(&self.0) }
}
struct Empty;
impl Serialize for Empty {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { s.serialize_map(Some(0))?.end() }
}
fn present<T: Serialize, M: SerializeMap>(map: &mut M, key: &str, fact: ContextFact<T>) -> Result<(), M::Error> {
    if let ContextFact::Present(value) = fact { map.serialize_entry(key, &value)?; }
    Ok(())
}
fn state<T>(fact: ContextFact<T>) -> Option<&'static str> {
    match fact {
        ContextFact::Present(_) => None,
        ContextFact::NotEstablished => Some("not_established"),
        ContextFact::NotApplicable => Some("not_applicable"),
        ContextFact::Unavailable => Some("unavailable"),
    }
}
#[derive(Serialize)]
struct Application { application: ContextLabel }
struct Request<'a>(&'a RequestExecutionView);
impl Serialize for Request<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        map.serialize_entry("localRequestId", &StringId(self.0.inner.root.local))?;
        present(&mut map, "requestId", self.0.diagnostic_snapshot().request)?;
        if let Some(ingress) = &self.0.inner.ingress {
            map.serialize_entry("callId", &ingress.call_id)?;
            map.serialize_entry("deadlineUnixMs", &ingress.deadline_unix_ms)?;
        }
        map.end()
    }
}
struct Interface<'a>(&'a RequestExecutionView);
impl Serialize for Interface<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        map.serialize_entry("entry", "profusegw")?;
        if let Some(identity) = self.0.identity() { map.serialize_entry("interfaceId", &identity.route)?; }
        map.end()
    }
}
struct Execution<'a>(&'a RequestExecutionView);
impl Serialize for Execution<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let facts = self.0.inner.facts;
        let mut map = s.serialize_map(None)?;
        if let ContextFact::Present(task) = facts.task { map.serialize_entry("taskId", &StringId(task))?; }
        if let ContextFact::Present(scope) = facts.scope { map.serialize_entry("scopeId", &StringId(scope.as_u64()))?; }
        present(&mut map, "dbOperation", facts.db_operation)?;
        map.serialize_entry("phase", &facts.phase)?;
        map.end()
    }
}
struct Call<'a>(&'a RequestExecutionView);
impl Serialize for Call<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        if let Some(child) = &self.0.inner.child {
            map.serialize_entry("spanId", &SpanProjection(child.span))?;
            map.serialize_entry("attempt", &child.attempt)?;
            map.serialize_entry("service", &child.service)?;
            map.serialize_entry("method", &child.operation)?;
        }
        map.end()
    }
}
struct Trace<'a> { view: &'a RequestExecutionView, source: Option<JsonSubtree<'a>> }
impl Serialize for Trace<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        if let Some(source) = self.source {
            for (key, value) in source.children() {
                let key = key.ok_or_else(|| serde::ser::Error::custom("trace is not an object"))?;
                if key != "traceId" && key != "rpcId" { map.serialize_entry(key, &value)?; }
            }
        }
        let snapshot = self.view.diagnostic_snapshot();
        present(&mut map, "traceId", snapshot.trace_id)?;
        present(&mut map, "rpcId", snapshot.rpc_id)?;
        map.end()
    }
}
#[derive(Serialize)]
struct Input<'a> {
    #[serde(rename = "requestData", skip_serializing_if = "Option::is_none")]
    request_data: Option<JsonSubtree<'a>>,
}
#[derive(Serialize)]
struct Auth<'a> {
    state: &'static str,
    #[serde(rename = "subjectId", skip_serializing_if = "Option::is_none")]
    subject: Option<JsonSubtree<'a>>,
}
struct Meta<'a> { view: &'a RequestExecutionView, tree: Option<JsonSubtree<'a>> }
struct FieldStates<'a> { view: &'a RequestExecutionView, tree: Option<JsonSubtree<'a>> }
impl Serialize for Meta<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        map.serialize_entry("revision", &StringId(self.view.inner.revision))?;
        if let ContextFact::Present(scope) = self.view.inner.facts.scope { map.serialize_entry("scopeId", &StringId(scope.as_u64()))?; }
        map.serialize_entry("fieldStates", &FieldStates { view: self.view, tree: self.tree })?;
        map.serialize_entry("redactions", &Redactions(self.view))?;
        map.end()
    }
}
struct Redactions<'a>(&'a RequestExecutionView);
impl Serialize for Redactions<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        if let Some(source) = &self.0.inner.business {
            let mut result = Ok(());
            source.with_secrets(&mut |paths| {
                for path in paths {
                    if result.is_err() { break; }
                    result = map.serialize_entry(path, "secret");
                }
            });
            result?;
        }
        map.end()
    }
}
struct ProtectedSource<'a>(&'a dyn crate::context_json::ContextSource);
impl Serialize for ProtectedSource<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut serializer = Some(s);
        let mut result = None;
        self.0.with_tree_and_secrets(&mut |tree, secrets| {
            let value = crate::context_json::RedactedSubtree::new(tree, secrets, "/business");
            result = Some(value.serialize(serializer.take().expect("single business read")));
        });
        result.expect("business source provides one immutable tree")
    }
}
impl Serialize for FieldStates<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let mut map = s.serialize_map(None)?;
        for path in [
            "/traceInfo/traceId", "/traceInfo/rpcId", "/requestInfo/requestId", "/interfaceInfo/interfaceId",
            "/interfaceInfo/module", "/interfaceInfo/service", "/interfaceInfo/operation",
            "/requestInfo/callId", "/requestInfo/deadlineUnixMs", "/inputInfo/requestData", "/ldcInfo", "/authInfo/subjectId",
            "/profuseContext", "/executionInfo/taskId", "/executionInfo/scopeId", "/executionInfo/dbOperation",
            "/callInfo/callId", "/callInfo/parentCallId", "/callInfo/kind", "/callInfo/dependencyAlias", "/callInfo/service",
            "/callInfo/method", "/callInfo/contractSha256", "/callInfo/spanId", "/callInfo/attempt",
        ] {
            if let Some(state) = select::state_at(self.view, self.tree, path) { map.serialize_entry(path, state)?; }
        }
        map.end()
    }
}
impl UnifiedContext<'_> {
    fn serialize_tree<S: Serializer>(&self, s: S, tree: Option<JsonSubtree<'_>>) -> Result<S::Ok, S::Error> {
        let select = |path| tree.and_then(|tree| tree.pointer(path).ok().flatten());
        let mut map = s.serialize_map(None)?;
        map.serialize_entry("schemaVersion", "1.0")?;
        map.serialize_entry("applicationInfo", &Application { application: self.0.inner.root.application })?;
        map.serialize_entry("traceInfo", &Trace { view: self.0, source: select("/profuseGwContext/traceInfo") })?;
        if let Some(ldc) = select("/profuseGwContext/ldcInfo") { map.serialize_entry("ldcInfo", &ldc)?; }
        map.serialize_entry("requestInfo", &Request(self.0))?;
        map.serialize_entry("interfaceInfo", &Interface(self.0))?;
        map.serialize_entry("inputInfo", &Input { request_data: select("/requestData") })?;
        map.serialize_entry("callInfo", &Call(self.0))?;
        map.serialize_entry("executionInfo", &Execution(self.0))?;
        map.serialize_entry("authInfo", &Auth { state: "unavailable", subject: select("/profuseGwContext/userInfo/userId") })?;
        if let Some(profuse) = select("/profuseContext") { map.serialize_entry("profuseContext", &profuse)?; }
        if let Some(business) = &self.0.inner.business {
            map.serialize_entry("business", &ProtectedSource(business.as_ref()))?;
        } else { map.serialize_entry("business", &Empty)?; }
        map.serialize_entry("_meta", &Meta { view: self.0, tree })?;
        map.end()
    }
}
impl Serialize for UnifiedContext<'_> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        if let Some(ingress) = &self.0.inner.ingress {
            let mut serializer = Some(s);
            let mut result = None;
            ingress.source.with_tree(&mut |tree| {
                result = Some(self.serialize_tree(serializer.take().expect("single context read"), Some(tree)));
            });
            result.expect("context source must provide one immutable tree")
        } else { self.serialize_tree(s, None) }
    }
}