cog_task/server/task/
block.rs1use crate::action::Action;
2use crate::comm::SignalId;
3use crate::resource::ResourceAddr;
4use crate::server::{config::OptionalConfig, Config, State};
5use crate::util::Hash;
6use eyre::{eyre, Result};
7use serde::{Deserialize, Serialize};
8use serde_cbor::ser::to_vec_packed;
9use serde_cbor::Value;
10use std::collections::BTreeMap;
11
12#[derive(Deserialize, Serialize, Debug)]
13pub struct Block {
14 name: String,
15 #[serde(default)]
16 #[serde(alias = "cfg")]
17 config: OptionalConfig,
18 tree: Box<dyn Action>,
19 #[serde(default)]
20 state: BTreeMap<SignalId, Value>,
21}
22
23impl Block {
24 pub fn init(&mut self) -> Result<()> {
25 self.verify_name()?;
26 self.verify_connections()?;
27 Ok(())
28 }
29
30 fn verify_name(&self) -> Result<()> {
31 if self.name.is_empty() {
32 Err(eyre!("Block `name` cannot be the empty string."))
33 } else if !self
34 .name
35 .chars()
36 .all(|c| c.is_alphabetic() || c.is_alphanumeric() | "+-_() ".contains(c))
37 {
38 Err(eyre!(
39 "Block `name` characters need to be alphanumeric or one of (+-_() ): '{}'",
40 self.name
41 ))
42 } else {
43 Ok(())
44 }
45 }
46
47 fn verify_connections(&self) -> Result<()> {
48 let mut in_signals = self.tree.in_signals();
49 let mut out_signals = self.tree.out_signals();
50
51 in_signals.insert(0);
52 out_signals.insert(0);
53
54 let in_not_out: Vec<_> = in_signals.difference(&out_signals).collect();
55 let out_not_in: Vec<_> = out_signals.difference(&in_signals).collect();
56
57 if !in_not_out.is_empty() {
58 Err(eyre!("Consumed signals are never produced: {in_not_out:?}"))
59 } else if !out_not_in.is_empty() {
60 Err(eyre!("Produced signals are never consumed: {out_not_in:?}"))
61 } else {
62 Ok(())
63 }
64 }
65
66 pub fn resources(&self, config: &Config) -> Vec<ResourceAddr> {
67 self.tree.resources(config)
68 }
69
70 #[inline(always)]
71 pub fn action_tree(&self) -> &dyn Action {
72 &*self.tree
73 }
74
75 #[inline(always)]
76 pub fn action_tree_vec(&self) -> Vec<u8> {
77 to_vec_packed(&self.tree).unwrap()
78 }
79
80 #[inline(always)]
81 pub fn default_state(&self) -> &State {
82 &self.state
83 }
84
85 #[inline(always)]
86 pub fn label(&self) -> &str {
87 &self.name
88 }
89
90 #[inline]
91 pub fn config(&self, base_config: &Config) -> Config {
92 self.config.fill_blanks(base_config)
93 }
94}
95
96impl Hash for Block {
97 fn hash(&self) -> String {
98 use sha2::{Digest, Sha256};
99 let mut hasher = Sha256::default();
100 hasher.update(&serde_cbor::to_vec(&(&self.tree, &self.config)).unwrap());
101 hex::encode(hasher.finalize())
102 }
103}