sig-net 0.7.0

Sig-Net secure CoAP-based DMX512 lighting control protocol library
Documentation
use crate::*;

fn option_nibble(value: u16) -> (u8, u8) {
    match value {
        0..=12 => (value as u8, 0),
        13..=268 => (13, 1),
        _ => (14, 2),  // >= 269
    }
}

fn encode_option_nibble(buffer: &mut PacketBuffer, delta: u16, length: u16) -> Result<()> {
    let (delta_nib, delta_ext) = option_nibble(delta);
    let (len_nib, len_ext) = option_nibble(length);

    let nibble = (delta_nib << 4) | len_nib;
    buffer.write_byte(nibble)?;

    match delta_ext {
        1 => buffer.write_byte((delta - 13) as u8)?,
        2 => buffer.write_u16(delta - 269)?,
        _ => {}
    }

    match len_ext {
        1 => buffer.write_byte((length - 13) as u8)?,
        2 => buffer.write_u16(length - 269)?,
        _ => {}
    }

    Ok(())
}

pub fn encode_coap_option(
    buffer: &mut PacketBuffer,
    option_number: u16,
    prev_option: u16,
    option_value: &[u8],
) -> Result<()> {
    let delta = option_number
        .checked_sub(prev_option)
        .ok_or(SigNetError::InvalidArgument)?;
    encode_option_nibble(buffer, delta, option_value.len() as u16)?;
    buffer.write_bytes(option_value)
}

pub fn build_coap_header(buffer: &mut PacketBuffer, message_id: u16) -> Result<()> {
    let header = CoAPHeader::new(message_id);
    buffer.write_bytes(&header.to_bytes())
}

pub fn build_uri_path_options(buffer: &mut PacketBuffer, universe: u16, scope: &str) -> Result<()> {
    if !(MIN_UNIVERSE..=MAX_UNIVERSE).contains(&universe) {
        return Err(SigNetError::InvalidArgument);
    }

    let segments = [
        SIGNET_URI_PREFIX,
        SIGNET_URI_VERSION,
        scope,
        SIGNET_URI_LEVEL,
    ];

    let mut prev_option: u16 = 0;
    for &segment in &segments {
        encode_coap_option(buffer, COAP_OPTION_URI_PATH, prev_option, segment.as_bytes())?;
        prev_option = COAP_OPTION_URI_PATH;
    }

    let universe_str = universe.to_string();
    encode_coap_option(buffer, COAP_OPTION_URI_PATH, prev_option, universe_str.as_bytes())
}

pub fn build_uri_string(universe: u16, scope: &str, uri_output: &mut [u8]) -> Result<usize> {
    if !(MIN_UNIVERSE..=MAX_UNIVERSE).contains(&universe) {
        return Err(SigNetError::InvalidArgument);
    }
    let s = format!(
        "/{}/{}/{}/{}/{}",
        SIGNET_URI_PREFIX,
        SIGNET_URI_VERSION,
        scope,
        SIGNET_URI_LEVEL,
        universe
    );
    let bytes = s.as_bytes();
    if bytes.len() > uri_output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    uri_output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}

pub fn build_node_uri_string(
    tuid: &[u8; TUID_LENGTH],
    endpoint: u16,
    scope: &str,
    uri_output: &mut [u8],
) -> Result<usize> {
    let hex = TUID(*tuid).to_hex_upper();
    let hex_str = core::str::from_utf8(&hex)
        .map_err(|_| SigNetError::Encode)?;
    let s = format!(
        "/{}/{}/{}/{}/{}/{}",
        SIGNET_URI_PREFIX,
        SIGNET_URI_VERSION,
        scope,
        SIGNET_URI_NODE,
        hex_str,
        endpoint
    );
    let bytes = s.as_bytes();
    if bytes.len() > uri_output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    uri_output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}

/// /sig-net/v1/<scope>/node_beacon/{TUID_UPPER}/0
pub fn build_node_beacon_uri_string(
    tuid: &[u8; TUID_LENGTH],
    scope: &str,
    output: &mut [u8],
) -> Result<usize> {
    let hex = TUID(*tuid).to_hex_upper();
    let hex_str = core::str::from_utf8(&hex)
        .map_err(|_| SigNetError::Encode)?;
    let s = format!(
        "/{}/{}/{}/node_beacon/{}/0",
        SIGNET_URI_PREFIX, SIGNET_URI_VERSION, scope, hex_str
    );
    let bytes = s.as_bytes();
    if bytes.len() > output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}

/// /sig-net/v1/<scope>/node_lost/{TUID_UPPER}/0
pub fn build_node_lost_uri_string(
    tuid: &[u8; TUID_LENGTH],
    scope: &str,
    output: &mut [u8],
) -> Result<usize> {
    let hex = TUID(*tuid).to_hex_upper();
    let hex_str = core::str::from_utf8(&hex)
        .map_err(|_| SigNetError::Encode)?;
    let s = format!(
        "/{}/{}/{}/node_lost/{}/0",
        SIGNET_URI_PREFIX, SIGNET_URI_VERSION, scope, hex_str
    );
    let bytes = s.as_bytes();
    if bytes.len() > output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}

/// /sig-net/v1/<scope>/manager/{TUID_UPPER}/{endpoint}
pub fn build_manager_uri_string(
    tuid: &[u8; TUID_LENGTH],
    endpoint: u16,
    scope: &str,
    output: &mut [u8],
) -> Result<usize> {
    let hex = TUID(*tuid).to_hex_upper();
    let hex_str = core::str::from_utf8(&hex)
        .map_err(|_| SigNetError::Encode)?;
    let s = format!(
        "/{}/{}/{}/manager/{}/{}",
        SIGNET_URI_PREFIX, SIGNET_URI_VERSION, scope, hex_str, endpoint
    );
    let bytes = s.as_bytes();
    if bytes.len() > output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}

/// /sig-net/v1/<scope>/timecode/{stream}
pub fn build_timecode_uri_string(
    stream: u8,
    scope: &str,
    output: &mut [u8],
) -> Result<usize> {
    let s = format!(
        "/{}/{}/{}/timecode/{}",
        SIGNET_URI_PREFIX, SIGNET_URI_VERSION, scope, stream
    );
    let bytes = s.as_bytes();
    if bytes.len() > output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}

/// /sig-net/v1/<scope>/preview/{universe}
pub fn build_preview_uri_string(
    universe: u16,
    scope: &str,
    output: &mut [u8],
) -> Result<usize> {
    if !(MIN_UNIVERSE..=MAX_UNIVERSE).contains(&universe) {
        return Err(SigNetError::InvalidArgument);
    }
    let s = format!(
        "/{}/{}/{}/preview/{}",
        SIGNET_URI_PREFIX, SIGNET_URI_VERSION, scope, universe
    );
    let bytes = s.as_bytes();
    if bytes.len() > output.len() {
        return Err(SigNetError::InvalidArgument);
    }
    output[..bytes.len()].copy_from_slice(bytes);
    Ok(bytes.len())
}