Skip to main content

tauri_plugin_auditaur/
ipc.rs

1use serde::Deserialize;
2
3/// Reserved Tauri invoke argument used by Auditaur's experimental IPC trace bridge.
4pub const IPC_CONTEXT_ARG: &str = "auditaurTraceContext";
5
6/// W3C trace context carried from Auditaur's frontend invoke wrapper.
7///
8/// Add this as an optional `auditaur_trace_context` argument on Tauri commands
9/// that should continue frontend invoke traces in backend `tracing` spans.
10#[derive(Debug, Clone, Deserialize)]
11pub struct IpcTraceContext {
12    traceparent: Option<String>,
13}
14
15impl IpcTraceContext {
16    /// Returns the valid W3C `traceparent` value, if one was provided.
17    pub fn traceparent(&self) -> Option<&str> {
18        self.traceparent
19            .as_deref()
20            .filter(|value| is_w3c_traceparent(value))
21    }
22}
23
24/// Extracts a `traceparent` field value for use in `#[tracing::instrument]`.
25pub fn ipc_traceparent(context: Option<&IpcTraceContext>) -> &str {
26    context
27        .and_then(IpcTraceContext::traceparent)
28        .unwrap_or_default()
29}
30
31fn is_w3c_traceparent(value: &str) -> bool {
32    let mut parts = value.split('-');
33    let version = parts.next();
34    let trace_id = parts.next();
35    let parent_span_id = parts.next();
36    let flags = parts.next();
37    parts.next().is_none()
38        && version.is_some_and(|value| is_hex_len(value, 2))
39        && trace_id.is_some_and(|value| is_hex_len(value, 32))
40        && parent_span_id.is_some_and(|value| is_hex_len(value, 16))
41        && flags.is_some_and(|value| is_hex_len(value, 2))
42}
43
44fn is_hex_len(value: &str, len: usize) -> bool {
45    value.len() == len && value.bytes().all(|byte| byte.is_ascii_hexdigit())
46}
47
48#[cfg(test)]
49mod tests {
50    use super::{ipc_traceparent, IpcTraceContext};
51    use serde_json::json;
52
53    #[test]
54    fn accepts_valid_traceparent() {
55        let context = IpcTraceContext {
56            traceparent: Some(
57                "00-00112233445566778899aabbccddeeff-0123456789abcdef-01".to_string(),
58            ),
59        };
60
61        assert_eq!(
62            ipc_traceparent(Some(&context)),
63            "00-00112233445566778899aabbccddeeff-0123456789abcdef-01"
64        );
65    }
66
67    #[test]
68    fn ignores_invalid_traceparent() {
69        let context = IpcTraceContext {
70            traceparent: Some("not-a-traceparent".to_string()),
71        };
72
73        assert_eq!(ipc_traceparent(Some(&context)), "");
74        assert_eq!(ipc_traceparent(None), "");
75    }
76
77    #[test]
78    fn deserializes_missing_or_extra_fields_safely() {
79        let missing: IpcTraceContext = serde_json::from_value(json!({})).unwrap();
80        let extra: IpcTraceContext = serde_json::from_value(json!({
81            "traceparent": "00-00112233445566778899aabbccddeeff-0123456789abcdef-01",
82            "future": true
83        }))
84        .unwrap();
85
86        assert_eq!(ipc_traceparent(Some(&missing)), "");
87        assert_eq!(
88            ipc_traceparent(Some(&extra)),
89            "00-00112233445566778899aabbccddeeff-0123456789abcdef-01"
90        );
91    }
92}