vihaco_runtime/
generated.rs1use eyre::Result;
5
6use crate::Effects;
7
8pub trait GeneratedComponent {
9 type Instruction;
10 type Message;
11 type Effect;
12
13 fn execute_generated(
14 &mut self,
15 inst: &Self::Instruction,
16 msg: Self::Message,
17 ) -> Result<Effects<Self::Effect>>;
18}
19
20pub trait HasInstructionSet {
22 type Runtime;
24
25 type Syntax;
27}
28
29#[cfg(not(feature = "typed-declarations"))]
34pub trait Component: HasInstructionSet {}
35
36#[cfg(feature = "typed-declarations")]
38pub trait Component {}
39
40#[cfg(not(feature = "typed-declarations"))]
42pub trait Composite: HasInstructionSet {}
43
44#[cfg(feature = "typed-declarations")]
46pub trait Composite {}
47
48pub fn expect_exactly_one_effect<E>(effects: Effects<E>) -> Result<E> {
49 match effects {
50 Effects::None => Err(eyre::eyre!("expected exactly one effect, got 0")),
51 Effects::One(effect) => Ok(effect),
52 Effects::Many(mut effects) => match effects.len() {
53 0 => Err(eyre::eyre!("expected exactly one effect, got 0")),
54 1 => Ok(effects.pop().expect("one effect is present")),
55 effect_count => Err(eyre::eyre!(
56 "expected exactly one effect, got {}",
57 effect_count
58 )),
59 },
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct CompositeMetadata {
65 pub devices: &'static [vihaco_abi::metadata::DeviceMetadata],
66 pub source_symbol_aliases: &'static [vihaco_abi::metadata::SourceSymbolAliasMetadata],
67}
68
69impl CompositeMetadata {
70 pub fn devices(&self) -> &'static [vihaco_abi::metadata::DeviceMetadata] {
71 self.devices
72 }
73
74 pub fn device_by_name(
75 &self,
76 name: &str,
77 ) -> Option<&'static vihaco_abi::metadata::DeviceMetadata> {
78 self.devices.iter().find(|device| device.name == name)
79 }
80
81 pub fn source_symbol_aliases(
82 &self,
83 ) -> &'static [vihaco_abi::metadata::SourceSymbolAliasMetadata] {
84 self.source_symbol_aliases
85 }
86
87 pub fn source_symbol_device_code(&self, name: &str) -> Option<u8> {
88 if let Some(alias) = self
89 .source_symbol_aliases
90 .iter()
91 .find(|alias| alias.name == name)
92 {
93 return Some(alias.device_code);
94 }
95 self.device_by_name(name).map(|device| device.code)
96 }
97
98 pub fn validate_source_symbols<I, V, Ty, Info>(
99 &self,
100 module: &vihaco_module::module::LocalModule<I, V, Ty, Info>,
101 ) -> Result<()> {
102 let unresolved: Vec<String> = module
103 .source_symbols
104 .iter()
105 .filter(|symbol| self.source_symbol_device_code(&symbol.name).is_none())
106 .map(|symbol| format!("{}: {}", symbol.index, symbol.name))
107 .collect();
108
109 if unresolved.is_empty() {
110 return Ok(());
111 }
112
113 Err(eyre::eyre!(
114 "module declares unresolved source symbols for this machine: {}",
115 unresolved.join(", ")
116 ))
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::expect_exactly_one_effect;
123 use crate::Effects;
124
125 #[test]
126 fn expect_exactly_one_effect_accepts_single_effect() {
127 let effect = expect_exactly_one_effect(Effects::one(7)).unwrap();
128
129 assert_eq!(effect, 7);
130 }
131
132 #[test]
133 fn expect_exactly_one_effect_rejects_zero_effects() {
134 let err = expect_exactly_one_effect::<u8>(Effects::none()).unwrap_err();
135
136 assert_eq!(err.to_string(), "expected exactly one effect, got 0");
137 }
138
139 #[test]
140 fn expect_exactly_one_effect_rejects_multiple_effects() {
141 let err = expect_exactly_one_effect(Effects::from(vec![1, 2])).unwrap_err();
142
143 assert_eq!(err.to_string(), "expected exactly one effect, got 2");
144 }
145
146 #[test]
147 fn expect_exactly_one_effect_accepts_one_item_in_many() {
148 let effects = Effects::Many([7].into_iter().collect());
149
150 assert_eq!(expect_exactly_one_effect(effects).unwrap(), 7);
151 }
152
153 #[test]
154 fn expect_exactly_one_effect_rejects_empty_many() {
155 let effects = Effects::Many([].into_iter().collect());
156 let err = expect_exactly_one_effect::<u8>(effects).unwrap_err();
157
158 assert_eq!(err.to_string(), "expected exactly one effect, got 0");
159 }
160}