Skip to main content

arora_module_core/
lib.rs

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
18/// Analyzes a module from the path where it is written in the YAML format.
19/// See [`analyze_module`].
20pub 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
30/// Analyzes a module by reading its header and
31/// resolves its dependencies with the help of the provided registry.
32/// Produces a list of assets that can be used for code generation.
33/// First, the types, then the modules, then the imports.
34pub 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    // Resolve the module contents into a description compatible with the registry.
42    // It already includes the dependencies (internal and external) as references.
43    let executor_name = module_definition.executor.name.to_owned();
44    let resolved_module = resolve_high_module(module_definition, registry).await?;
45
46    // Collect first the actual types behind the references.
47    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    // Then publish imports, and then this module.
71    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/// Assets are records provided or referred to by a module.
82#[derive(Debug, Serialize, Deserialize)]
83pub enum ModuleAsset {
84    /// Type, including its identifier.
85    Type(Uuid, Version, TypeDefinitionFrozen),
86    /// Imported symbol, including the identifier of its origin module.
87    Import(ImportAsset),
88    /// Module, including its identifier, version and executor type.
89    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    /// Record is not known to the registry or registry is not available.
154    RegistryError(RegistryError),
155
156    /// IO error.
157    IoError(std::io::Error),
158
159    /// Serialization / deserialization error.
160    YAMLError(serde_yaml::Error),
161
162    /// Virtual file system error.
163    VfsError(VfsError),
164
165    /// For any other error.
166    #[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}