reactive_graph_sys_binary/behaviour/component/
save_binary_data.rs

1use std::fs::OpenOptions;
2use std::io::Write;
3use std::path::Path;
4
5use base64::Engine;
6use base64::engine::general_purpose::STANDARD;
7use reactive_graph_behaviour_model_api::behaviour_validator;
8use reactive_graph_behaviour_model_api::prelude::*;
9use reactive_graph_behaviour_model_impl::entity_behaviour;
10use reactive_graph_graph::prelude::*;
11use reactive_graph_reactive_model_impl::ReactiveEntity;
12use reactive_graph_runtime_model::ActionProperties::TRIGGER;
13use serde_json::Value;
14use uuid::Uuid;
15
16use reactive_graph_sys_binary_model::BinaryDataProperties::DATA_URL;
17use reactive_graph_sys_file_model::FileProperties::FILENAME;
18
19entity_behaviour!(
20    SaveBinaryData,
21    SaveBinaryDataFactory,
22    SaveBinaryDataFsm,
23    SaveBinaryDataBehaviourTransitions,
24    SaveBinaryDataValidator
25);
26
27behaviour_validator!(SaveBinaryDataValidator, Uuid, ReactiveEntity, TRIGGER.as_ref(), FILENAME.as_ref(), DATA_URL.as_ref());
28
29impl BehaviourInit<Uuid, ReactiveEntity> for SaveBinaryDataBehaviourTransitions {}
30
31impl BehaviourConnect<Uuid, ReactiveEntity> for SaveBinaryDataBehaviourTransitions {
32    fn connect(&self) -> Result<(), BehaviourConnectFailed> {
33        let reactive_instance = self.reactive_instance.clone();
34        self.property_observers.observe_with_handle(TRIGGER.as_ref(), move |trigger: &Value| {
35            if !trigger.as_bool().unwrap_or(false) {
36                return;
37            }
38            if let Some(filename) = reactive_instance.as_string(FILENAME) {
39                let filename = shellexpand::tilde(&filename);
40                let path = Path::new(filename.as_ref());
41
42                if let Some(data_url) = reactive_instance.get(DATA_URL).and_then(|v| v.as_str().map(String::from)) {
43                    // The next operations may be performance intensive
44                    // Decode DataURL with BASE64 encoding to byte array
45                    let mut parts = data_url.splitn(2, ',');
46                    let _part_data_url_prefix = parts.next();
47                    let bytes = match parts.next() {
48                        Some(part_base64_encoded_data) => STANDARD.decode(part_base64_encoded_data).ok(),
49                        None => None,
50                    };
51                    if bytes.is_none() {
52                        return;
53                    }
54
55                    // Write byte array to file
56                    if let Ok(mut file) = OpenOptions::new().write(true).create(true).truncate(true).open(path) {
57                        let _success = file.write_all(bytes.unwrap().as_slice());
58                    }
59                }
60            }
61        });
62        Ok(())
63    }
64}
65
66impl BehaviourShutdown<Uuid, ReactiveEntity> for SaveBinaryDataBehaviourTransitions {}
67impl BehaviourTransitions<Uuid, ReactiveEntity> for SaveBinaryDataBehaviourTransitions {}