1use crate::PackageError;
4
5pub const RESERVED_MODULE_NAMES: &[&str] = &["aion_flow_ffi"];
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct BeamModule {
17 pub name: String,
19 pub bytes: Vec<u8>,
21}
22
23impl BeamModule {
24 #[must_use]
26 pub fn new(name: impl Into<String>, bytes: impl Into<Vec<u8>>) -> Self {
27 Self {
28 name: name.into(),
29 bytes: bytes.into(),
30 }
31 }
32
33 #[must_use]
35 pub fn name(&self) -> &str {
36 &self.name
37 }
38
39 #[must_use]
41 pub fn bytes(&self) -> &[u8] {
42 &self.bytes
43 }
44}
45
46#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct BeamSet {
49 modules: Vec<BeamModule>,
50}
51
52impl BeamSet {
53 pub fn new(mut modules: Vec<BeamModule>) -> Result<Self, PackageError> {
64 if let Some(reserved) = modules
65 .iter()
66 .find(|module| RESERVED_MODULE_NAMES.contains(&module.name.as_str()))
67 {
68 return Err(PackageError::ReservedModuleName {
69 module: reserved.name.clone(),
70 });
71 }
72 modules.sort_by(|left, right| left.name.cmp(&right.name));
73
74 if let Some(duplicate) = modules
75 .windows(2)
76 .find(|pair| pair[0].name == pair[1].name)
77 .map(|pair| pair[0].name.clone())
78 {
79 return Err(PackageError::MalformedBeamEntry { entry: duplicate });
80 }
81
82 Ok(Self { modules })
83 }
84
85 #[must_use]
87 pub fn len(&self) -> usize {
88 self.modules.len()
89 }
90
91 #[must_use]
93 pub fn is_empty(&self) -> bool {
94 self.modules.is_empty()
95 }
96
97 pub fn iter(&self) -> impl Iterator<Item = &BeamModule> {
99 self.modules.iter()
100 }
101
102 #[must_use]
104 pub fn get(&self, name: &str) -> Option<&[u8]> {
105 self.modules
106 .binary_search_by(|module| module.name.as_str().cmp(name))
107 .ok()
108 .map(|index| self.modules[index].bytes.as_slice())
109 }
110}
111
112impl IntoIterator for BeamSet {
113 type IntoIter = std::vec::IntoIter<BeamModule>;
114 type Item = BeamModule;
115
116 fn into_iter(self) -> Self::IntoIter {
117 self.modules.into_iter()
118 }
119}
120
121impl<'a> IntoIterator for &'a BeamSet {
122 type IntoIter = std::slice::Iter<'a, BeamModule>;
123 type Item = &'a BeamModule;
124
125 fn into_iter(self) -> Self::IntoIter {
126 self.modules.iter()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::{BeamModule, BeamSet};
133 use crate::PackageError;
134
135 #[test]
136 fn beam_set_order_is_independent_of_insertion_order() -> Result<(), PackageError> {
137 let first = BeamSet::new(vec![
138 BeamModule::new("workflow/c", vec![3]),
139 BeamModule::new("workflow/a", vec![1]),
140 BeamModule::new("workflow/b", vec![2]),
141 ])?;
142 let second = BeamSet::new(vec![
143 BeamModule::new("workflow/b", vec![2]),
144 BeamModule::new("workflow/c", vec![3]),
145 BeamModule::new("workflow/a", vec![1]),
146 ])?;
147
148 let first_names: Vec<&str> = first.iter().map(BeamModule::name).collect();
149 let second_names: Vec<&str> = second.iter().map(BeamModule::name).collect();
150
151 assert_eq!(first_names, vec!["workflow/a", "workflow/b", "workflow/c"]);
152 assert_eq!(first_names, second_names);
153 assert_eq!(first, second);
154
155 Ok(())
156 }
157
158 #[test]
159 fn beam_set_rejects_duplicate_logical_names() {
160 let result = BeamSet::new(vec![
161 BeamModule::new("workflow/a", vec![1]),
162 BeamModule::new("workflow/a", vec![2]),
163 ]);
164
165 assert!(matches!(
166 result,
167 Err(PackageError::MalformedBeamEntry { entry }) if entry == "workflow/a"
168 ));
169 }
170
171 #[test]
172 fn beam_set_rejects_engine_reserved_namespaces() {
173 let result = BeamSet::new(vec![
174 BeamModule::new("workflow/a", vec![1]),
175 BeamModule::new("aion_flow_ffi", vec![2]),
176 ]);
177
178 assert!(matches!(
179 result,
180 Err(PackageError::ReservedModuleName { module }) if module == "aion_flow_ffi"
181 ));
182 }
183
184 #[test]
185 fn lookup_returns_exact_bytes_by_logical_name() -> Result<(), PackageError> {
186 let bytes = vec![0, 1, 2, 3, 255];
187 let beams = BeamSet::new(vec![BeamModule::new("workflow/a", bytes.clone())])?;
188
189 assert_eq!(beams.get("workflow/a"), Some(bytes.as_slice()));
190 assert_eq!(beams.get("workflow/missing"), None);
191
192 Ok(())
193 }
194}