tuioxide 0.3.1

A Rust implementation of the TUIO 1.1 and TUIO 2.0 protocols, providing both client and server components for sending and receiving multitouch and tangible object data over OSC.
Documentation
use std::collections::HashSet;

use log::warn;
use rosc::{OscMessage, OscType};

use crate::{
    core::TuioError,
    tuio11::{blob::BlobProfile, cursor::CursorProfile, object::ObjectProfile},
};

#[derive(Debug, Clone, Default, Copy)]
pub(crate) enum TuioBundleType {
    Cursor,
    Object,
    Blob,
    #[default]
    Unknown,
}

#[derive(Debug, Clone, PartialEq)]
pub(crate) enum EntityType {
    Cursor(Vec<CursorProfile>),
    Object(Vec<ObjectProfile>),
    Blob(Vec<BlobProfile>),
}

#[derive(Debug, Clone, Default)]
pub(crate) struct TuioBundle {
    tuio_type: TuioBundleType,
    source: Option<String>,
    alive: HashSet<i32>,
    set: Option<EntityType>,
    fseq: i32,
}

impl TuioBundle {
    pub(crate) fn profile_type(&self) -> TuioBundleType {
        self.tuio_type
    }

    pub(crate) fn source(&self) -> &Option<String> {
        &self.source
    }

    pub(crate) fn alive(&self) -> &HashSet<i32> {
        &self.alive
    }

    pub(crate) fn tuio_entities(&self) -> &Option<EntityType> {
        &self.set
    }

    pub(crate) fn fseq(&self) -> i32 {
        self.fseq
    }

    pub(crate) fn set_type(&mut self, tuio_type: TuioBundleType) {
        self.tuio_type = tuio_type
    }

    pub(crate) fn set_source(&mut self, message: &OscMessage) {
        self.source = message.args.get(1).and_then(|arg| arg.clone().string());
    }

    pub(crate) fn set_alive(&mut self, message: &OscMessage) {
        self.alive = message
            .args
            .iter()
            .skip(1)
            .filter_map(|e| e.clone().int())
            .collect();
    }

    pub(crate) fn set_set(&mut self, message: &OscMessage) -> Result<(), TuioError> {
        match &self.tuio_type {
            TuioBundleType::Cursor => {
                if let EntityType::Cursor(set) =
                    self.set.get_or_insert(EntityType::Cursor(Vec::new()))
                {
                    if message.args.len() != 7 {
                        return Err(TuioError::MissingArguments(message.clone()));
                    }
                    let cursor = CursorProfile::try_from(message)?;
                    set.push(cursor);
                }
            }
            TuioBundleType::Object => {
                if let EntityType::Object(set) =
                    self.set.get_or_insert(EntityType::Object(Vec::new()))
                {
                    if message.args.len() != 11 {
                        return Err(TuioError::MissingArguments(message.clone()));
                    }
                    let object = ObjectProfile::try_from(message)?;
                    set.push(object);
                }
            }
            TuioBundleType::Blob => {
                if let EntityType::Blob(set) = self.set.get_or_insert(EntityType::Blob(Vec::new()))
                {
                    if message.args.len() != 13 {
                        return Err(TuioError::MissingArguments(message.clone()));
                    }
                    let blob = BlobProfile::try_from(message)?;
                    set.push(blob);
                }
            }
            TuioBundleType::Unknown => {
                warn!("Unknown Tuio Bundle Type")
            }
        }
        Ok(())
    }

    pub(crate) fn set_fseq(&mut self, message: &OscMessage) -> Result<(), TuioError> {
        if let Some(OscType::Int(fseq)) = message.args.get(1) {
            self.fseq = *fseq;
            Ok(())
        } else {
            Err(TuioError::MissingArguments(message.clone()))
        }
    }
}