ifacialmocap 0.1.0

An iFacialMocap data protocol parser
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use regex::Regex;
#[derive(Serialize, Deserialize, Debug)]
pub struct Value {
    #[serde(flatten)]
    pub int_keys: HashMap<String, i32>,
    #[serde(flatten)]
    pub xyz_keys: HashMap<String, [f32; 3]>,
    #[serde(flatten)]
    pub coord_keys: HashMap<String, ([f32; 3],[f32; 3])>

}


impl Value {
    fn new() -> Self {
        Value {
            int_keys: HashMap::new(),
            xyz_keys: HashMap::new(),
            coord_keys: HashMap::new()
        }
    }
    pub fn parse_str<S: Into<String>>(s: S) -> Result<Self, String> {
        let mut value = Value::new();

        let regex = Regex::new(r"[^\|?]*").unwrap();
        let str = s.into();
        let capt = regex.captures_iter(&str).collect::<Vec<_>>();

        let error_message = "Invalid value string: ".to_string();

        for cap in capt {
            cap.iter().for_each(|x| {
                let x = x.unwrap().as_str();
                let key = if x.contains('#') {
                    x.split_once('#').unwrap_or(("", "0")).0
                } else {
                    //println!("{:?}", x.split_once("-"));
                    x.split_once('-').unwrap_or(("", "0")).0
                };

                let val_str = if x.contains('#') {
                    // value is array of string
                    let val = x.split_once('#').unwrap_or(("", "0")).1;
                    val.split(',').map(|x| x.to_string()).collect::<Vec<String>>()
                } else {
                    // value is a single value
                    vec![x.split_once('-').unwrap_or(("", "0")).1.to_string()]
                };

                if val_str.len() == 1 {
                    // parse value as integer
                    let val = val_str[0].parse::<i32>().map_err(|_| error_message.clone()).unwrap();
                    value.int_keys.insert(key.to_string(), val);
                } else if val_str.len() == 3 {
                    // parse value as xyz
                    let val = [
                        val_str[0].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
                        val_str[1].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
                        val_str[2].parse::<f32>().map_err(|_| error_message.clone()).unwrap()
                    ];
                    value.xyz_keys.insert(key.to_string(), val);
                } else if val_str.len() == 6 {
                    // parse value as coord
                    let val = ([
                        val_str[0].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
                        val_str[1].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
                        val_str[2].parse::<f32>().map_err(|_| error_message.clone()).unwrap()
                    ], [
                        val_str[3].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
                        val_str[4].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
                        val_str[5].parse::<f32>().map_err(|_| error_message.clone()).unwrap()
                    ]);
                    value.coord_keys.insert(key.to_string(), val);
                }
            } );
        }
        value.int_keys.remove("");

        Ok(value)
    }

    pub fn parse_str_lossy<S: Into<String>>(s: S) -> Result<Self, String> {
        let mut value = Value::new();
        let regex = Regex::new(r"[^\|?]*").unwrap();
        let str = s.into();
        let capt = regex.captures_iter(&str).collect::<Vec<_>>();

        for cap in capt {
            cap.iter().for_each(|x| {
                let x = x.unwrap().as_str();
                let key = if x.contains('#') {
                    x.split_once('#').unwrap_or(("", "0")).0
                } else {
                    //println!("{:?}", x.split_once("-"));
                    x.split_once('-').unwrap_or(("", "0")).0
                };

                let val_str = if x.contains('#') {
                    // value is array of string
                    let val = x.split_once('#').unwrap_or(("", "0")).1;
                    val.split(',').map(|x| x.to_string()).collect::<Vec<String>>()
                } else {
                    // value is a single value
                    vec![x.split_once('-').unwrap_or(("", "0")).1.to_string()]
                };

                if val_str.len() == 1 {
                    // parse value as integer
                    let val = val_str[0].parse::<i32>().unwrap_or(0);
                    value.int_keys.insert(key.to_string(), val);
                } else if val_str.len() == 3 {
                    // parse value as xyz
                    let val = [
                        val_str[0].parse::<f32>().unwrap_or(0.0),
                        val_str[1].parse::<f32>().unwrap_or(0.0),
                        val_str[2].parse::<f32>().unwrap_or(0.0),
                    ];
                    value.xyz_keys.insert(key.to_string(), val);
                } else if val_str.len() == 6 {
                    // parse value as coord
                    let val = ([
                        val_str[0].parse::<f32>().unwrap_or(0.0),
                        val_str[1].parse::<f32>().unwrap_or(0.0),
                        val_str[2].parse::<f32>().unwrap_or(0.0),
                    ], [
                        val_str[3].parse::<f32>().unwrap_or(0.0),
                        val_str[4].parse::<f32>().unwrap_or(0.0),
                        val_str[5].parse::<f32>().unwrap_or(0.0),
                    ]);
                    value.coord_keys.insert(key.to_string(), val);
                }
            } );
        }
        value.int_keys.remove("");

        Ok(value)
    }
}


#[cfg(test)]
mod tests {
    use crate::Value;

    static TEST_DATA: &str = "mouthSmile_R-0|eyeLookOut_L-0|mouthUpperUp_L-11|eyeWide_R-0|mouthClose-8|mouthPucker-4|mouthRollLower-9|eyeBlink_R-7|eyeLookDown_L-17|cheekSquint_R-11|eyeBlink_L-7|tongueOut-0|jawRight-0|eyeLookIn_R-6|cheekSquint_L-11|mouthDimple_L-10|mouthPress_L-4|eyeSquint_L-11|mouthRight-0|mouthShrugLower-9|eyeLookUp_R-0|eyeLookOut_R-0|mouthPress_R-5|cheekPuff-2|jawForward-11|mouthLowerDown_L-9|mouthFrown_L-6|mouthShrugUpper-26|browOuterUp_L-4|browInnerUp-20|mouthDimple_R-10|browDown_R-0|mouthUpperUp_R-10|mouthRollUpper-8|mouthFunnel-12|mouthStretch_R-21|mouthFrown_R-13|eyeLookDown_R-17|jawOpen-12|jawLeft-0|browDown_L-0|mouthSmile_L-0|noseSneer_R-18|mouthLowerDown_R-8|noseSneer_L-21|eyeWide_L-0|mouthStretch_L-21|browOuterUp_R-4|eyeLookIn_L-4|eyeSquint_R-11|eyeLookUp_L-0|mouthLeft-1|=head#-21.488958,-6.038993,-6.6019735,-0.030653415,-0.10287084,-0.6584072|rightEye#6.0297494,2.4403017,0.25649446|leftEye#6.034903,-1.6660284,-0.17520553|";
    static INVALID_DATA: &str = "mouthLeft-0|browInnerUp-6|mouthLowerDown_L-4|mouthDimple_R-2|mouthFunnel-5|eyeSquint_L-12|browOuterUp_L-0|mouthUpperUp_L-4|mouthFrown_R-2|eyeLookOut_R-0|mouthShrugUpper-11|eyeSquint_R-12|eyeLookDown_R-15|mouthRollLower-6|eyeLookDown_L-16|cheekSquint_L-9|mouthSmile_L-0|mouthRight-0|mouthDimple_L-2|jawRight-0|mouthPucker-24|mouthRollUpper-1|mouthPress_L-8|eyeLookOut_L-0|browDown_R-13|cheekSquint_R-8|mouthFrown_L-3|tongueOut-0|mouthPress_R-10|browDown_L-12|mouthLowerDown_R-4|eyeWide_L-2|cheekPuff-7|mouthSmile_R-0|eyeLookIn_L-0|eyeLookUp_L-0|jawForward-3|jawLeft-4|noseSneer_L-13|jawOpen-2|mouthStretch_R-8|eyeLookUp_R-0|mouthClose-4|eyeWide_R-2|eyeBlink_L-2|eyeLookIn_R-12|noseSneer_R-9|eyeBlink_R-2|mouthUpperUp_R-4|browOuterUp_R-0|mouthStretch_L-9|mouthShrugLower-14|hapihapi-0|=head#25.409164,-5.085786,3.8090365,0.052303925,0.2366666,-0.0259732|rightEye#5.2707267,4.227702,0.41178665|leftEye#5.300755,0.32921365,0.03218361|-0.67254096|||||3|";

    #[test]
    fn it_works() {
        let a = Value::parse_str(TEST_DATA);
        assert!(a.is_ok());
    }
    #[test]
    fn lossy_test() {
        let a = Value::parse_str_lossy(INVALID_DATA);
        assert!(a.is_ok());
    }
}