1pub mod header;
2pub mod resolve;
3use arora_registry::{ModuleFrozen, ReadableRegistry, RegistryError, TypeDefinitionFrozen};
4use arora_types::module::high::ModuleDefinition;
5use arora_types::record::ty::FrozenTy;
6use arora_types::record::{module::frozen::Export, FrozenReference, Resolver};
7use arora_types::record::{RecordType, Selector};
8use arora_vfs::VfsError;
9use bytes::{Buf, BufMut};
10use derive_more::Display;
11use resolve::resolve_high_module;
12use semver::{Version, VersionReq};
13use serde::{de::DeserializeOwned, Deserialize, Serialize};
14use std::collections::HashSet;
15use std::fs::read_to_string;
16use std::path::Path;
17use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
18use uuid::Uuid;
19
20pub async fn analyze_module_from_path<P: AsRef<Path>, R: ReadableRegistry + Resolver>(
23 path: P,
24 registry: &mut R,
25) -> Result<Vec<ModuleAsset>, ModuleDeclarationError> {
26 let module_yaml = read_to_string(path).map_err(ModuleDeclarationError::IoError)?;
27 let module_definition: ModuleDefinition =
28 serde_yaml::from_str(&module_yaml).map_err(ModuleDeclarationError::YAMLError)?;
29 analyze_module(module_definition, registry).await
30}
31
32pub async fn analyze_module<R: ReadableRegistry + Resolver>(
37 module_definition: ModuleDefinition,
38 registry: &mut R,
39) -> Result<Vec<ModuleAsset>, ModuleDeclarationError> {
40 let module_id = module_definition.id;
41 let module_version = module_definition.version.clone();
42
43 let executor_name = module_definition.executor.name.to_owned();
46 let resolved_module = resolve_high_module(module_definition, registry).await?;
47
48 let mut assets = Vec::new();
56 let mut seen: HashSet<Uuid> = HashSet::new();
57 let mut pending: Vec<FrozenReference> = resolved_module.module.dependencies.to_vec();
58 while let Some(reference) = pending.pop() {
59 if !seen.insert(reference.id) {
62 continue;
63 }
64 if arora_types::ty::WELL_KNOWN_IDS.contains(&reference.id) {
68 continue;
69 }
70 let selector = Selector::Id(reference.id);
71 let record_type = registry
72 .type_of(&selector)
73 .await
74 .map_err(ModuleDeclarationError::RegistryError)?;
75 if !matches!(record_type, RecordType::Structure | RecordType::Enumeration) {
76 continue;
77 }
78 let type_def = registry
79 .get_type(
80 &selector,
81 &VersionReq::parse(reference.version.to_string().as_str()).unwrap(),
82 )
83 .await
84 .map_err(ModuleDeclarationError::RegistryError)?;
85 collect_type_references(&type_def, &mut pending);
87 assets.push(ModuleAsset::Type(
88 reference.id,
89 reference.version.0.to_owned(),
90 type_def,
91 ));
92 }
93
94 assets.extend(resolved_module.imports.into_iter().map(ModuleAsset::Import));
96 assets.push(ModuleAsset::Module(
97 module_id,
98 module_version.into(),
99 resolved_module.module,
100 executor_name,
101 ));
102 Ok(assets)
103}
104
105fn collect_type_references(type_def: &TypeDefinitionFrozen, out: &mut Vec<FrozenReference>) {
109 let push_ty = |ty: &FrozenTy, out: &mut Vec<FrozenReference>| match ty {
110 FrozenTy::Primitive(_) => {}
111 FrozenTy::FrozenScalar(scalar) => out.push(scalar.reference.to_owned()),
112 FrozenTy::FrozenArray(array) => out.push(array.reference.to_owned()),
113 };
114 match type_def {
115 TypeDefinitionFrozen::Primitive(_) => {}
116 TypeDefinitionFrozen::Structure(structure) => {
117 for field in structure.fields.values() {
118 push_ty(&field.ty, out);
119 }
120 }
121 TypeDefinitionFrozen::Enumeration(enumeration) => {
122 for variant in enumeration.variants.values() {
123 push_ty(&variant.ty, out);
124 }
125 }
126 }
127}
128
129#[derive(Debug, Serialize, Deserialize)]
131pub enum ModuleAsset {
132 Type(Uuid, Version, TypeDefinitionFrozen),
134 Import(ImportAsset),
136 Module(Uuid, Version, ModuleFrozen, String),
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ImportAsset {
142 pub module_id: Uuid,
143 pub tag: Version,
144 pub module_name: String,
145 pub id: Uuid,
146 pub import: Export,
147}
148
149pub struct Writer<'a, W: AsyncWrite + Unpin> {
150 writer: &'a mut W,
151}
152
153impl<'a, W: AsyncWrite + Unpin> Writer<'a, W> {
154 pub fn new(writer: &'a mut W) -> Self {
155 Self { writer }
156 }
157
158 pub async fn write<T: Serialize>(&mut self, value: T) -> tokio::io::Result<()> {
159 let mut size = [0u8; 4];
160 let serialized = serde_json::to_string(&value).unwrap();
161 (&mut size[..]).put_u32(serialized.len() as u32);
162 self.writer.write_all(&size).await?;
163 self.writer.write_all(serialized.as_bytes()).await?;
164 Ok(())
165 }
166
167 pub async fn end(self) -> tokio::io::Result<()> {
168 let mut size = [0u8; 4];
169 (&mut size[..]).put_u32(0);
170 self.writer.write_all(&size).await?;
171 Ok(())
172 }
173}
174
175pub struct Reader<'a, R: AsyncRead + Unpin> {
176 reader: &'a mut R,
177}
178
179impl<'a, R: AsyncRead + Unpin> Reader<'a, R> {
180 pub fn new(reader: &'a mut R) -> Self {
181 Self { reader }
182 }
183
184 pub async fn read<T: DeserializeOwned>(&mut self) -> tokio::io::Result<Option<T>> {
185 let mut size = [0u8; 4];
186 self.reader.read_exact(&mut size).await?;
187 let size = (&size[..]).get_u32() as usize;
188 if size == 0 {
189 return Ok(None);
190 }
191
192 let mut buf = vec![0u8; size];
193 self.reader.read_exact(&mut buf).await?;
194 let value: T = serde_json::from_slice(&buf).unwrap();
195 Ok(Some(value))
196 }
197}
198
199#[derive(Display, Debug)]
200pub enum ModuleDeclarationError {
201 RegistryError(RegistryError),
203
204 IoError(std::io::Error),
206
207 YAMLError(serde_yaml::Error),
209
210 VfsError(VfsError),
212
213 #[display("error: {}", _0)]
215 Generic(String),
216}
217
218impl std::error::Error for ModuleDeclarationError {}
219
220#[cfg(test)]
221mod tests {
222 use arora_types::module::high::ModuleDefinition;
223 use std::str::FromStr;
224 use uuid::Uuid;
225
226 #[test]
227 fn parse_uuid() {
228 let uuid_string = "b41899c3-66dc-40d4-ab61-d1ccf5231c88";
229 let expected = Uuid::from_str(uuid_string).unwrap();
230 let actual: Uuid = serde_yaml::from_str(uuid_string).unwrap();
231 assert!(actual == expected);
232 }
233
234 #[test]
235 fn load_simple_module() {
236 let module_string = "id: 325c5e47-32db-4e23-a38f-7a2849647e0c
237author: Semio
238description: Test C++ module
239license: Proprietary
240name: test-cpp-2
241version:
242 major: 0
243 minor: 1
244 patch: 0
245executor:
246 name: wasm
247exports:
248 - type: function
249 id: 07f5740c-ba4a-45af-8ec5-bedde5737e99
250 name: test
251 parameters:
252 - id: b41899c3-66dc-40d4-ab61-d1ccf5231c88
253 name: a
254 type:
255 kind: scalar
256 id: Status
257 - id: 63086e48-804f-403a-8862-3358ddedc08d
258 name: b
259 type:
260 kind: scalar
261 id: i32
262 ret:
263 kind: scalar
264 id: i32
265imports: []
266dependencies: []
267executable_mime: application/wasm";
268
269 let header: ModuleDefinition = serde_yaml::from_str(module_string).unwrap();
270 assert!(header.name == "test-cpp-2");
271 }
272}