alpine-protocol-sdk 0.2.4

High-level SDK on top of the ALPINE protocol layer.
Documentation
use std::collections::HashMap;

use alpine::messages::{CapabilitySet, ChannelFormat};
use alpine::profile::StreamProfile;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CapabilityMatchMode {
    Strict,
    Lenient,
}

#[derive(Debug, Clone)]
pub struct CapabilityValidation {
    pub warnings: Vec<String>,
}

#[derive(Debug, Clone, Default)]
pub struct CapabilityDeprecationRegistry {
    vendor_extensions: HashMap<String, String>,
}

impl CapabilityDeprecationRegistry {
    pub fn new() -> Self {
        Self {
            vendor_extensions: HashMap::new(),
        }
    }

    pub fn register_vendor_extension(
        &mut self,
        name: impl Into<String>,
        message: impl Into<String>,
    ) {
        self.vendor_extensions.insert(name.into(), message.into());
    }

    pub fn warnings_for(&self, caps: &CapabilitiesSummary) -> Vec<String> {
        let mut warnings = Vec::new();
        if let Some(extensions) = caps.vendor_extensions.as_ref() {
            for (name, msg) in self.vendor_extensions.iter() {
                if extensions.contains_key(name) {
                    warnings.push(format!("capability '{}' deprecated: {}", name, msg));
                }
            }
        }
        warnings
    }
}

#[derive(Debug, Clone)]
pub struct CapabilitiesSummary {
    pub channel_formats: Vec<ChannelFormat>,
    pub max_channels: u32,
    pub grouping_supported: bool,
    pub streaming_supported: bool,
    pub encryption_supported: bool,
    pub vendor_extensions: Option<HashMap<String, serde_json::Value>>,
}

impl From<&CapabilitySet> for CapabilitiesSummary {
    fn from(caps: &CapabilitySet) -> Self {
        Self {
            channel_formats: caps.channel_formats.clone(),
            max_channels: caps.max_channels,
            grouping_supported: caps.grouping_supported,
            streaming_supported: caps.streaming_supported,
            encryption_supported: caps.encryption_supported,
            vendor_extensions: caps.vendor_extensions.clone(),
        }
    }
}

impl CapabilitiesSummary {
    pub fn supports_format(&self, format: &ChannelFormat) -> bool {
        self.channel_formats.contains(format)
    }

    pub fn validate_stream(
        &self,
        format: ChannelFormat,
        channel_count: usize,
    ) -> Result<(), String> {
        if !self.streaming_supported {
            return Err("streaming not supported by device".into());
        }
        if !self.supports_format(&format) {
            return Err(format!("channel format {:?} not supported", format));
        }
        if channel_count as u32 > self.max_channels {
            return Err(format!(
                "channel count {} exceeds device max {}",
                channel_count, self.max_channels
            ));
        }
        Ok(())
    }

    pub fn validate_stream_with_mode(
        &self,
        format: ChannelFormat,
        channel_count: usize,
        mode: CapabilityMatchMode,
    ) -> Result<CapabilityValidation, String> {
        match mode {
            CapabilityMatchMode::Strict => {
                self.validate_stream(format, channel_count)?;
                Ok(CapabilityValidation {
                    warnings: Vec::new(),
                })
            }
            CapabilityMatchMode::Lenient => {
                let mut warnings = Vec::new();
                if !self.streaming_supported {
                    warnings.push("streaming not supported by device".to_string());
                }
                if !self.supports_format(&format) {
                    warnings.push(format!("channel format {:?} not supported", format));
                }
                if channel_count as u32 > self.max_channels {
                    warnings.push(format!(
                        "channel count {} exceeds device max {}",
                        channel_count, self.max_channels
                    ));
                }
                Ok(CapabilityValidation { warnings })
            }
        }
    }

    pub fn deprecation_warnings(&self, registry: &CapabilityDeprecationRegistry) -> Vec<String> {
        registry.warnings_for(self)
    }

    pub fn validate_profile(&self, profile: &StreamProfile) -> Result<(), String> {
        if !self.streaming_supported {
            return Err("streaming not supported by device".into());
        }
        profile
            .clone()
            .compile()
            .map(|_| ())
            .map_err(|err| err.to_string())
    }
}