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 x.split_once('-').unwrap_or(("", "0")).0
41 };
42
43 let val_str = if x.contains('#') {
44 let val = x.split_once('#').unwrap_or(("", "0")).1;
46 val.split(',').map(|x| x.to_string()).collect::<Vec<String>>()
47 } else {
48 vec![x.split_once('-').unwrap_or(("", "0")).1.to_string()]
50 };
51
52 if val_str.len() == 1 {
53 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 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 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 x.split_once('-').unwrap_or(("", "0")).0
98 };
99
100 let val_str = if x.contains('#') {
101 let val = x.split_once('#').unwrap_or(("", "0")).1;
103 val.split(',').map(|x| x.to_string()).collect::<Vec<String>>()
104 } else {
105 vec![x.split_once('-').unwrap_or(("", "0")).1.to_string()]
107 };
108
109 if val_str.len() == 1 {
110 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 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 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}