ifacialmocap/
lib.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use regex::Regex;
4#[derive(Serialize, Deserialize, Debug)]
5pub struct Value {
6    #[serde(flatten)]
7    pub int_keys: HashMap<String, i32>,
8    #[serde(flatten)]
9    pub xyz_keys: HashMap<String, [f32; 3]>,
10    #[serde(flatten)]
11    pub coord_keys: HashMap<String, ([f32; 3],[f32; 3])>
12
13}
14
15
16impl Value {
17    fn new() -> Self {
18        Value {
19            int_keys: HashMap::new(),
20            xyz_keys: HashMap::new(),
21            coord_keys: HashMap::new()
22        }
23    }
24    pub fn parse_str<S: Into<String>>(s: S) -> Result<Self, String> {
25        let mut value = Value::new();
26
27        let regex = Regex::new(r"[^\|?]*").unwrap();
28        let str = s.into();
29        let capt = regex.captures_iter(&str).collect::<Vec<_>>();
30
31        let error_message = "Invalid value string: ".to_string();
32
33        for cap in capt {
34            cap.iter().for_each(|x| {
35                let x = x.unwrap().as_str();
36                let key = if x.contains('#') {
37                    x.split_once('#').unwrap_or(("", "0")).0
38                } else {
39                    //println!("{:?}", x.split_once("-"));
40                    x.split_once('-').unwrap_or(("", "0")).0
41                };
42
43                let val_str = if x.contains('#') {
44                    // value is array of string
45                    let val = x.split_once('#').unwrap_or(("", "0")).1;
46                    val.split(',').map(|x| x.to_string()).collect::<Vec<String>>()
47                } else {
48                    // value is a single value
49                    vec![x.split_once('-').unwrap_or(("", "0")).1.to_string()]
50                };
51
52                if val_str.len() == 1 {
53                    // parse value as integer
54                    let val = val_str[0].parse::<i32>().map_err(|_| error_message.clone()).unwrap();
55                    value.int_keys.insert(key.to_string(), val);
56                } else if val_str.len() == 3 {
57                    // parse value as xyz
58                    let val = [
59                        val_str[0].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
60                        val_str[1].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
61                        val_str[2].parse::<f32>().map_err(|_| error_message.clone()).unwrap()
62                    ];
63                    value.xyz_keys.insert(key.to_string(), val);
64                } else if val_str.len() == 6 {
65                    // parse value as coord
66                    let val = ([
67                        val_str[0].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
68                        val_str[1].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
69                        val_str[2].parse::<f32>().map_err(|_| error_message.clone()).unwrap()
70                    ], [
71                        val_str[3].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
72                        val_str[4].parse::<f32>().map_err(|_| error_message.clone()).unwrap(),
73                        val_str[5].parse::<f32>().map_err(|_| error_message.clone()).unwrap()
74                    ]);
75                    value.coord_keys.insert(key.to_string(), val);
76                }
77            } );
78        }
79        value.int_keys.remove("");
80
81        Ok(value)
82    }
83
84    pub fn parse_str_lossy<S: Into<String>>(s: S) -> Result<Self, String> {
85        let mut value = Value::new();
86        let regex = Regex::new(r"[^\|?]*").unwrap();
87        let str = s.into();
88        let capt = regex.captures_iter(&str).collect::<Vec<_>>();
89
90        for cap in capt {
91            cap.iter().for_each(|x| {
92                let x = x.unwrap().as_str();
93                let key = if x.contains('#') {
94                    x.split_once('#').unwrap_or(("", "0")).0
95                } else {
96                    //println!("{:?}", x.split_once("-"));
97                    x.split_once('-').unwrap_or(("", "0")).0
98                };
99
100                let val_str = if x.contains('#') {
101                    // value is array of string
102                    let val = x.split_once('#').unwrap_or(("", "0")).1;
103                    val.split(',').map(|x| x.to_string()).collect::<Vec<String>>()
104                } else {
105                    // value is a single value
106                    vec![x.split_once('-').unwrap_or(("", "0")).1.to_string()]
107                };
108
109                if val_str.len() == 1 {
110                    // parse value as integer
111                    let val = val_str[0].parse::<i32>().unwrap_or(0);
112                    value.int_keys.insert(key.to_string(), val);
113                } else if val_str.len() == 3 {
114                    // parse value as xyz
115                    let val = [
116                        val_str[0].parse::<f32>().unwrap_or(0.0),
117                        val_str[1].parse::<f32>().unwrap_or(0.0),
118                        val_str[2].parse::<f32>().unwrap_or(0.0),
119                    ];
120                    value.xyz_keys.insert(key.to_string(), val);
121                } else if val_str.len() == 6 {
122                    // parse value as coord
123                    let val = ([
124                        val_str[0].parse::<f32>().unwrap_or(0.0),
125                        val_str[1].parse::<f32>().unwrap_or(0.0),
126                        val_str[2].parse::<f32>().unwrap_or(0.0),
127                    ], [
128                        val_str[3].parse::<f32>().unwrap_or(0.0),
129                        val_str[4].parse::<f32>().unwrap_or(0.0),
130                        val_str[5].parse::<f32>().unwrap_or(0.0),
131                    ]);
132                    value.coord_keys.insert(key.to_string(), val);
133                }
134            } );
135        }
136        value.int_keys.remove("");
137
138        Ok(value)
139    }
140}
141
142
143#[cfg(test)]
144mod tests {
145    use crate::Value;
146
147    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|";
148    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|";
149
150    #[test]
151    fn it_works() {
152        let a = Value::parse_str(TEST_DATA);
153        assert!(a.is_ok());
154    }
155    #[test]
156    fn lossy_test() {
157        let a = Value::parse_str_lossy(INVALID_DATA);
158        assert!(a.is_ok());
159    }
160}