foldit_plugin_sdk/
decode.rs1use std::collections::HashMap;
4
5use crate::proto::plugin as proto;
6use crate::protocol::{DispatchContext, ParamValue, ResidueRef};
7
8#[allow(clippy::cast_possible_truncation)]
13#[must_use]
14pub fn dispatch_context_from_proto(p: Option<proto::DispatchContext>) -> DispatchContext {
15 let to_native = |refs: Vec<proto::ResidueRef>| -> Vec<ResidueRef> {
16 refs.into_iter()
17 .map(|r| ResidueRef {
18 entity_id: molex::EntityId::from_raw(r.entity_id as u32),
19 residue_index: r.residue_index,
20 })
21 .collect()
22 };
23 match p {
24 Some(p) => DispatchContext {
25 focused_entity_id: p
26 .focused_entity_id
27 .map(|raw| molex::EntityId::from_raw(raw as u32)),
28 selection: to_native(p.selection),
29 designable: to_native(p.designable),
30 },
31 None => DispatchContext::default(),
32 }
33}
34
35#[must_use]
38pub fn params_from_proto<S: std::hash::BuildHasher + Default>(
39 p: HashMap<String, proto::ParamValue, S>,
40) -> HashMap<String, ParamValue, S> {
41 p.into_iter()
42 .filter_map(|(k, v)| {
43 let value = v.value?;
44 let native = match value {
45 proto::param_value::Value::IntValue(i) => ParamValue::Int(i),
46 proto::param_value::Value::FloatValue(f) => ParamValue::Float(f),
47 proto::param_value::Value::BoolValue(b) => ParamValue::Bool(b),
48 proto::param_value::Value::StringValue(s) => ParamValue::String(s),
49 proto::param_value::Value::Vec3Value(v3) => ParamValue::Vec3([v3.x, v3.y, v3.z]),
50 };
51 Some((k, native))
52 })
53 .collect()
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn dispatch_context_decodes_ids_and_refs() {
62 let ctx = dispatch_context_from_proto(Some(proto::DispatchContext {
63 focused_entity_id: Some(7),
64 selection: vec![proto::ResidueRef {
65 entity_id: 7,
66 residue_index: 3,
67 }],
68 designable: vec![proto::ResidueRef {
69 entity_id: 2,
70 residue_index: 0,
71 }],
72 }));
73 assert_eq!(ctx.focused_entity_id, Some(molex::EntityId::from_raw(7)));
74 assert_eq!(ctx.selection.len(), 1);
75 assert_eq!(ctx.selection[0].entity_id, molex::EntityId::from_raw(7));
76 assert_eq!(ctx.selection[0].residue_index, 3);
77 assert_eq!(ctx.designable[0].entity_id, molex::EntityId::from_raw(2));
78 }
79
80 #[test]
81 fn dispatch_context_none_is_default() {
82 let ctx = dispatch_context_from_proto(None);
83 assert!(ctx.focused_entity_id.is_none());
84 assert!(ctx.selection.is_empty());
85 assert!(ctx.designable.is_empty());
86 }
87
88 #[test]
89 fn params_decode_each_variant_and_drop_unset() {
90 let mut p: HashMap<String, proto::ParamValue> = HashMap::new();
91 let _ = p.insert(
92 "i".to_owned(),
93 proto::ParamValue {
94 value: Some(proto::param_value::Value::IntValue(5)),
95 },
96 );
97 let _ = p.insert(
98 "f".to_owned(),
99 proto::ParamValue {
100 value: Some(proto::param_value::Value::FloatValue(1.5)),
101 },
102 );
103 let _ = p.insert(
104 "b".to_owned(),
105 proto::ParamValue {
106 value: Some(proto::param_value::Value::BoolValue(true)),
107 },
108 );
109 let _ = p.insert(
110 "s".to_owned(),
111 proto::ParamValue {
112 value: Some(proto::param_value::Value::StringValue("x".to_owned())),
113 },
114 );
115 let _ = p.insert(
116 "v".to_owned(),
117 proto::ParamValue {
118 value: Some(proto::param_value::Value::Vec3Value(proto::Vec3 {
119 x: 1.0,
120 y: 2.0,
121 z: 3.0,
122 })),
123 },
124 );
125 let _ = p.insert("unset".to_owned(), proto::ParamValue { value: None });
126
127 let native = params_from_proto(p);
128 assert_eq!(native.len(), 5);
129 assert_eq!(native.get("i"), Some(&ParamValue::Int(5)));
130 assert_eq!(native.get("f"), Some(&ParamValue::Float(1.5)));
131 assert_eq!(native.get("b"), Some(&ParamValue::Bool(true)));
132 assert_eq!(native.get("s"), Some(&ParamValue::String("x".to_owned())));
133 assert_eq!(native.get("v"), Some(&ParamValue::Vec3([1.0, 2.0, 3.0])));
134 assert!(!native.contains_key("unset"));
135 }
136}