lxmf-wire 0.2.0

Core LXMF wire format, message primitives, and identity helpers for LXMF-rs.
Documentation
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;

pub fn normalize_display_name(value: &str) -> Option<String> {
    let trimmed = value.trim();
    if trimmed.is_empty() {
        return None;
    }
    if trimmed.chars().any(char::is_control) {
        return None;
    }

    let normalized: String = trimmed.chars().take(64).collect();
    if normalized.is_empty() {
        None
    } else {
        Some(normalized)
    }
}

pub fn encode_delivery_display_name_app_data(display_name: &str) -> Option<Vec<u8>> {
    let normalized = normalize_display_name(display_name)?;
    let peer_data =
        rmpv::Value::Array(vec![rmpv::Value::Binary(normalized.into_bytes()), rmpv::Value::Nil]);
    rmp_serde::to_vec(&peer_data).ok()
}

pub fn display_name_from_delivery_app_data(data: &[u8]) -> Option<String> {
    if data.is_empty() {
        return None;
    }

    let decoded: rmpv::Value = rmp_serde::from_slice(data).ok()?;
    match decoded {
        rmpv::Value::Array(values) => {
            let first = values.first()?;
            match first {
                rmpv::Value::Binary(bytes) => {
                    let raw = String::from_utf8(bytes.clone()).ok()?;
                    normalize_display_name(raw.as_str())
                }
                rmpv::Value::String(value) => normalize_display_name(value.as_str()?),
                _ => None,
            }
        }
        rmpv::Value::Binary(bytes) => {
            let raw = String::from_utf8(bytes).ok()?;
            normalize_display_name(raw.as_str())
        }
        rmpv::Value::String(value) => normalize_display_name(value.as_str()?),
        _ => None,
    }
}

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

    #[test]
    fn encode_and_decode_delivery_display_name_round_trip() {
        let encoded = encode_delivery_display_name_app_data("Alice Router").expect("encoded");
        let decoded = display_name_from_delivery_app_data(encoded.as_slice()).expect("decoded");
        assert_eq!(decoded, "Alice Router");
    }

    #[test]
    fn normalize_display_name_rejects_control_bytes() {
        assert!(normalize_display_name("Alice\nRouter").is_none());
    }
}