alibabacloud-rum 0.1.0

Alibaba Cloud RUM SDK for native Rust applications.
Documentation
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use uuid::Uuid;

#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct ParsedSw8 {
    pub trace_id: String,
    pub segment_id: String,
    pub parent_span_id: String,
    pub service: String,
    pub service_instance: String,
    pub endpoint: String,
    pub target_address: String,
}

pub(crate) fn encode_sw8(
    trace_id: &str,
    service: &str,
    service_instance: &str,
    endpoint: &str,
    target_address: &str,
) -> String {
    let segment_id = Uuid::new_v4().simple().to_string();
    encode_sw8_with_segment_id(
        trace_id,
        &segment_id,
        service,
        service_instance,
        endpoint,
        target_address,
    )
}

pub(crate) fn encode_sw8_with_segment_id(
    trace_id: &str,
    segment_id: &str,
    service: &str,
    service_instance: &str,
    endpoint: &str,
    target_address: &str,
) -> String {
    encode_sw8_with_ids(
        trace_id,
        segment_id,
        "0",
        service,
        service_instance,
        endpoint,
        target_address,
    )
}

pub(crate) fn encode_sw8_with_ids(
    trace_id: &str,
    segment_id: &str,
    span_id: &str,
    service: &str,
    service_instance: &str,
    endpoint: &str,
    target_address: &str,
) -> String {
    [
        "1".to_string(),
        enc(trace_id),
        enc(segment_id),
        span_id.to_string(),
        enc(service),
        enc(service_instance),
        enc(endpoint),
        enc(target_address),
    ]
    .join("-")
}

pub(crate) fn decode_sw8(value: &str) -> Option<ParsedSw8> {
    let parts = value.split('-').collect::<Vec<_>>();
    if parts.len() < 8 {
        return None;
    }
    Some(ParsedSw8 {
        trace_id: dec(parts[1])?,
        segment_id: dec(parts[2])?,
        parent_span_id: parts[3].to_string(),
        service: dec(parts[4])?,
        service_instance: dec(parts[5])?,
        endpoint: dec(parts[6])?,
        target_address: dec(parts[7])?,
    })
}

fn enc(value: &str) -> String {
    STANDARD.encode(value.as_bytes())
}

fn dec(value: &str) -> Option<String> {
    let bytes = STANDARD.decode(value).ok()?;
    String::from_utf8(bytes).ok()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sw8_round_trips_key_fields() {
        let sw8 = encode_sw8("abc", "svc", "inst", "HTTP GET", "example.com:443");
        let parsed = decode_sw8(&sw8).unwrap();
        assert_eq!(parsed.trace_id, "abc");
        assert_eq!(parsed.parent_span_id, "0");
        assert_eq!(parsed.service, "svc");
        assert_eq!(parsed.service_instance, "inst");
        assert_eq!(parsed.endpoint, "HTTP GET");
        assert_eq!(parsed.target_address, "example.com:443");
    }

    #[test]
    fn encodes_given_segment_id() {
        let sw8 =
            encode_sw8_with_segment_id("abc", "span", "svc", "inst", "HTTP GET", "example.com:443");
        let parsed = decode_sw8(&sw8).unwrap();
        assert_eq!(parsed.trace_id, "abc");
        assert_eq!(parsed.segment_id, "span");
        assert_eq!(parsed.parent_span_id, "0");
    }

    #[test]
    fn encodes_given_segment_and_span_ids() {
        let sw8 = encode_sw8_with_ids(
            "abc",
            "segment",
            "42",
            "svc",
            "inst",
            "HTTP GET",
            "example.com:443",
        );
        let parsed = decode_sw8(&sw8).unwrap();
        assert_eq!(parsed.trace_id, "abc");
        assert_eq!(parsed.segment_id, "segment");
        assert_eq!(parsed.parent_span_id, "42");
    }

    #[test]
    fn decodes_fixture_with_padding() {
        let sw8 = "1-YWJj-c2VnbWVudA==-0-c3Zj-aW5zdA==-SFRUUCBHRVQ=-ZXhhbXBsZS5jb206NDQz";
        let parsed = decode_sw8(sw8).unwrap();
        assert_eq!(parsed.trace_id, "abc");
        assert_eq!(parsed.segment_id, "segment");
        assert_eq!(parsed.parent_span_id, "0");
    }
}