1pub mod header;
2pub mod resolve;
3use arora_registry::{ModuleFrozen, ReadableRegistry, RegistryError, TypeDefinitionFrozen};
4use arora_types::module::high::ModuleDefinition;
5use arora_types::record::{module::frozen::Export, Resolver};
6use arora_types::record::{RecordType, Selector};
7use arora_vfs::VfsError;
8use bytes::{Buf, BufMut};
9use derive_more::Display;
10use resolve::resolve_high_module;
11use semver::{Version, VersionReq};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13use std::fs::read_to_string;
14use std::path::Path;
15use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
16use uuid::Uuid;
17
18pub async fn analyze_module_from_path<P: AsRef<Path>, R: ReadableRegistry + Resolver>(
21 path: P,
22 registry: &mut R,
23) -> Result<Vec<ModuleAsset>, ModuleDeclarationError> {
24 let module_yaml = read_to_string(path).map_err(ModuleDeclarationError::IoError)?;
25 let module_definition: ModuleDefinition =
26 serde_yaml::from_str(&module_yaml).map_err(ModuleDeclarationError::YAMLError)?;
27 analyze_module(module_definition, registry).await
28}
29
30pub async fn analyze_module<R: ReadableRegistry + Resolver>(
35 module_definition: ModuleDefinition,
36 registry: &mut R,
37) -> Result<Vec<ModuleAsset>, ModuleDeclarationError> {
38 let module_id = module_definition.id;
39 let module_version = module_definition.version.clone();
40
41 let executor_name = module_definition.executor.name.to_owned();
44 let resolved_module = resolve_high_module(module_definition, registry).await?;
45
46 let mut assets = Vec::new();
48 for dep_ref in &resolved_module.module.dependencies {
49 let selector = Selector::Id(dep_ref.id);
50 let record_type = registry
51 .type_of(&selector)
52 .await
53 .map_err(ModuleDeclarationError::RegistryError)?;
54 match record_type {
55 RecordType::Structure | RecordType::Enumeration => assets.push(ModuleAsset::Type(
56 dep_ref.id.to_owned(),
57 dep_ref.version.0.to_owned(),
58 registry
59 .get_type(
60 &selector,
61 &VersionReq::parse(dep_ref.version.to_string().as_str()).unwrap(),
62 )
63 .await
64 .map_err(ModuleDeclarationError::RegistryError)?,
65 )),
66 _ => (),
67 }
68 }
69
70 assets.extend(resolved_module.imports.into_iter().map(ModuleAsset::Import));
72 assets.push(ModuleAsset::Module(
73 module_id,
74 module_version.into(),
75 resolved_module.module,
76 executor_name,
77 ));
78 Ok(assets)
79}
80
81#[derive(Debug, Serialize, Deserialize)]
83pub enum ModuleAsset {
84 Type(Uuid, Version, TypeDefinitionFrozen),
86 Import(ImportAsset),
88 Module(Uuid, Version, ModuleFrozen, String),
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct ImportAsset {
94 pub module_id: Uuid,
95 pub tag: Version,
96 pub module_name: String,
97 pub id: Uuid,
98 pub import: Export,
99}
100
101pub struct Writer<'a, W: AsyncWrite + Unpin> {
102 writer: &'a mut W,
103}
104
105impl<'a, W: AsyncWrite + Unpin> Writer<'a, W> {
106 pub fn new(writer: &'a mut W) -> Self {
107 Self { writer }
108 }
109
110 pub async fn write<T: Serialize>(&mut self, value: T) -> tokio::io::Result<()> {
111 let mut size = [0u8; 4];
112 let serialized = serde_json::to_string(&value).unwrap();
113 (&mut size[..]).put_u32(serialized.len() as u32);
114 self.writer.write_all(&size).await?;
115 self.writer.write_all(serialized.as_bytes()).await?;
116 Ok(())
117 }
118
119 pub async fn end(self) -> tokio::io::Result<()> {
120 let mut size = [0u8; 4];
121 (&mut size[..]).put_u32(0);
122 self.writer.write_all(&size).await?;
123 Ok(())
124 }
125}
126
127pub struct Reader<'a, R: AsyncRead + Unpin> {
128 reader: &'a mut R,
129}
130
131impl<'a, R: AsyncRead + Unpin> Reader<'a, R> {
132 pub fn new(reader: &'a mut R) -> Self {
133 Self { reader }
134 }
135
136 pub async fn read<T: DeserializeOwned>(&mut self) -> tokio::io::Result<Option<T>> {
137 let mut size = [0u8; 4];
138 self.reader.read_exact(&mut size).await?;
139 let size = (&size[..]).get_u32() as usize;
140 if size == 0 {
141 return Ok(None);
142 }
143
144 let mut buf = vec![0u8; size];
145 self.reader.read_exact(&mut buf).await?;
146 let value: T = serde_json::from_slice(&buf).unwrap();
147 Ok(Some(value))
148 }
149}
150
151#[derive(Display, Debug)]
152pub enum ModuleDeclarationError {
153 RegistryError(RegistryError),
155
156 IoError(std::io::Error),
158
159 YAMLError(serde_yaml::Error),
161
162 VfsError(VfsError),
164
165 #[display("error: {}", _0)]
167 Generic(String),
168}
169
170impl std::error::Error for ModuleDeclarationError {}
171
172#[cfg(test)]
173mod tests {
174 use arora_types::module::high::ModuleDefinition;
175 use std::str::FromStr;
176 use uuid::Uuid;
177
178 #[test]
179 fn parse_uuid() {
180 let uuid_string = "b41899c3-66dc-40d4-ab61-d1ccf5231c88";
181 let expected = Uuid::from_str(uuid_string).unwrap();
182 let actual: Uuid = serde_yaml::from_str(uuid_string).unwrap();
183 assert!(actual == expected);
184 }
185
186 #[test]
187 fn load_simple_module() {
188 let module_string = "id: 325c5e47-32db-4e23-a38f-7a2849647e0c
189author: Semio
190description: Test C++ module
191license: Proprietary
192name: test-cpp-2
193version:
194 major: 0
195 minor: 1
196 patch: 0
197executor:
198 name: wasm
199exports:
200 - type: function
201 id: 07f5740c-ba4a-45af-8ec5-bedde5737e99
202 name: test
203 parameters:
204 - id: b41899c3-66dc-40d4-ab61-d1ccf5231c88
205 name: a
206 type:
207 kind: scalar
208 id: Status
209 - id: 63086e48-804f-403a-8862-3358ddedc08d
210 name: b
211 type:
212 kind: scalar
213 id: i32
214 ret:
215 kind: scalar
216 id: i32
217imports: []
218dependencies: []
219executable_mime: application/wasm";
220
221 let header: ModuleDefinition = serde_yaml::from_str(module_string).unwrap();
222 assert!(header.name == "test-cpp-2");
223 }
224}