1pub mod data;
7
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10
11use crate::blocks::InputImpl;
12
13use self::data::{BlockData, LinkData, ProgramMeta};
14
15use super::{engine::Engine, input::InputProps};
16
17type Reader = <InputImpl as InputProps>::Reader;
18type Writer = <InputImpl as InputProps>::Writer;
19
20pub trait EngineType = Engine<Reader = Reader, Writer = Writer> + Default;
21
22#[derive(Debug, Default, Clone, Serialize, Deserialize)]
23pub struct Program<E: EngineType> {
24 pub meta: ProgramMeta,
25 pub blocks: Vec<BlockData>,
26 pub links: Vec<LinkData>,
27 pub engine: E,
28}
29
30impl<E: EngineType> Program<E> {
31 pub fn new(name: &str, blocks: Vec<BlockData>, links: Vec<LinkData>) -> Self {
32 Self {
33 meta: ProgramMeta {
34 name: name.to_string(),
35 ..Default::default()
36 },
37 blocks,
38 links,
39 engine: E::default(),
40 }
41 }
42
43 pub fn load(&mut self) -> Result<()> {
44 self.engine.load_blocks_and_links(&self.blocks, &self.links)
45 }
46
47 pub async fn run(&mut self) {
48 self.engine.run().await
49 }
50}
51
52#[cfg(test)]
53mod test {
54 use crate::single_threaded::SingleThreadedEngine;
55
56 use super::{
57 data::{BlockData, LinkData},
58 Program,
59 };
60
61 #[test]
62 fn test_program_load() {
63 let mut program = Program::<SingleThreadedEngine>::new(
64 "test",
65 vec![
66 BlockData {
67 id: "00000000-0000-0000-0000-000000000000".to_string(),
68 name: "Add".to_string(),
69 dis: "Add".to_string(),
70 lib: "core".to_string(),
71 category: "maths".to_string(),
72 ver: "0.1.0".to_string(),
73 },
74 BlockData {
75 id: "00000000-0000-0000-0000-000000000001".to_string(),
76 name: "Add".to_string(),
77 dis: "Add".to_string(),
78 lib: "core".to_string(),
79 category: "maths".to_string(),
80 ver: "0.1.0".to_string(),
81 },
82 ],
83 vec![LinkData {
84 id: None,
85 source_block_uuid: "00000000-0000-0000-0000-000000000000".to_string(),
86 target_block_uuid: "00000000-0000-0000-0000-000000000001".to_string(),
87 source_block_pin_name: "out".to_string(),
88 target_block_pin_name: "in1".to_string(),
89 }],
90 );
91
92 assert!(program.load().is_ok());
93
94 assert!(program.engine.blocks().iter().all(|b| b.name() == "Add"));
95
96 assert!(program.engine.blocks()[0]
97 .get_output("out")
98 .unwrap()
99 .is_connected());
100
101 assert!(
102 program.engine.blocks()[0]
103 .get_output("out")
104 .unwrap()
105 .links()
106 .len()
107 == 1
108 );
109
110 assert!(program.engine.blocks()[0]
111 .get_output("out")
112 .unwrap()
113 .links()
114 .iter()
115 .any(
116 |l| l.target_block_id().to_string() == "00000000-0000-0000-0000-000000000001"
117 && l.target_input() == "in1"
118 ));
119 }
120
121 #[test]
122 fn test_program_load_invalid_block() {
123 let mut program = Program::<SingleThreadedEngine>::new(
124 "test",
125 vec![BlockData {
126 id: "00000000-0000-0000-0000-000000000000".to_string(),
127 name: "Missing".to_string(),
128 dis: "Missing".to_string(),
129 lib: "test".to_string(),
130 category: "maths".to_string(),
131 ver: "0.1.0".to_string(),
132 }],
133 vec![],
134 );
135
136 assert!(program.load().is_err());
137 }
138
139 #[test]
140 fn test_program_load_invalid_link() {
141 let blocks = vec![
142 BlockData {
143 id: "00000000-0000-0000-0000-000000000000".to_string(),
144 name: "Add".to_string(),
145 dis: "Add".to_string(),
146 lib: "test".to_string(),
147 category: "maths".to_string(),
148 ver: "0.1.0".to_string(),
149 },
150 BlockData {
151 id: "00000000-0000-0000-0000-000000000001".to_string(),
152 name: "Add".to_string(),
153 dis: "Add".to_string(),
154 lib: "test".to_string(),
155 category: "maths".to_string(),
156 ver: "0.1.0".to_string(),
157 },
158 ];
159
160 let mut program = Program::<SingleThreadedEngine>::new(
162 "test",
163 blocks.clone(),
164 vec![LinkData {
165 id: None,
166 source_block_uuid: "00000000-0000-0000-0000-000000000009".to_string(),
167 target_block_uuid: "00000000-0000-0000-0000-000000000001".to_string(),
168 source_block_pin_name: "out".to_string(),
169 target_block_pin_name: "in1".to_string(),
170 }],
171 );
172
173 assert!(program.load().is_err());
174
175 let mut program = Program::<SingleThreadedEngine>::new(
177 "test",
178 blocks.clone(),
179 vec![LinkData {
180 id: None,
181 source_block_uuid: "00000000-0000-0000-0000-000000000000".to_string(),
182 target_block_uuid: "00000000-0000-0000-0000-000000000001".to_string(),
183 source_block_pin_name: "missing".to_string(),
184 target_block_pin_name: "in1".to_string(),
185 }],
186 );
187
188 assert!(program.load().is_err());
189
190 let mut program = Program::<SingleThreadedEngine>::new(
192 "test",
193 blocks.clone(),
194 vec![LinkData {
195 id: None,
196 source_block_uuid: "00000000-0000-0000-0000-000000000000".to_string(),
197 target_block_uuid: "00000000-0000-0000-0000-000000000001".to_string(),
198 source_block_pin_name: "out".to_string(),
199 target_block_pin_name: "missing".to_string(),
200 }],
201 );
202
203 assert!(program.load().is_err());
204 }
205}