use std::collections::HashSet;
use crate::{ty::UNIT_ID, value::Value, SemanticVersion};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Executor {
pub name: String,
pub min_version: Option<SemanticVersion>,
pub max_version: Option<SemanticVersion>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum TypeRef {
Scalar { id: Uuid },
Array { id: Uuid },
Map { key_id: Uuid, value_id: Uuid },
}
impl TypeRef {
pub fn type_dependencies(&self) -> HashSet<Uuid> {
let mut deps = HashSet::new();
match self {
TypeRef::Scalar { id } => deps.insert(*id),
TypeRef::Array { id } => deps.insert(*id),
TypeRef::Map { key_id, value_id } => {
deps.insert(*key_id);
deps.insert(*value_id)
}
};
deps
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Parameter {
pub id: Uuid,
pub name: String,
#[serde(rename = "type")]
pub ty: TypeRef,
#[serde(default)]
pub mutable: bool,
pub default_value: Option<Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ExportFunction {
pub id: Uuid,
pub name: String,
#[serde(default)]
pub parameters: Vec<Parameter>,
#[serde(default = "default_return_type")]
pub ret: TypeRef,
}
fn default_return_type() -> TypeRef {
TypeRef::Scalar { id: *UNIT_ID }
}
impl ExportFunction {
pub fn type_dependencies(&self) -> HashSet<Uuid> {
let mut deps = HashSet::new();
for param in &self.parameters {
deps = deps.union(¶m.ty.type_dependencies()).cloned().collect();
}
deps = deps.union(&self.ret.type_dependencies()).cloned().collect();
deps
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ImportFunction {
pub module: Uuid,
pub id: Uuid,
pub name: String,
pub parameters: Vec<Parameter>,
pub ret: TypeRef,
}
impl ImportFunction {
pub fn type_dependencies(&self) -> HashSet<Uuid> {
let mut deps = HashSet::new();
for param in &self.parameters {
deps = deps.union(¶m.ty.type_dependencies()).cloned().collect();
}
deps = deps.union(&self.ret.type_dependencies()).cloned().collect();
deps
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ExportSymbol {
Function(ExportFunction),
}
impl ExportSymbol {
pub fn id(&self) -> &Uuid {
match self {
Self::Function(f) => &f.id,
}
}
pub fn name(&self) -> &String {
match self {
Self::Function(f) => &f.name,
}
}
pub fn type_dependencies(&self) -> HashSet<Uuid> {
match self {
Self::Function(f) => f.type_dependencies(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ImportSymbol {
Function(ImportFunction),
}
impl ImportSymbol {
pub fn module(&self) -> &Uuid {
match self {
Self::Function(f) => &f.module,
}
}
pub fn id(&self) -> &Uuid {
match self {
Self::Function(f) => &f.id,
}
}
pub fn name(&self) -> &String {
match self {
Self::Function(f) => &f.name,
}
}
pub fn type_dependencies(&self) -> HashSet<Uuid> {
match self {
Self::Function(f) => f.type_dependencies(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Header {
pub id: Uuid,
pub name: String,
pub author: String,
pub description: Option<String>,
pub license: String,
pub version: SemanticVersion,
pub executor: Executor,
pub exports: Vec<ExportSymbol>,
pub imports: Vec<ImportSymbol>,
pub executable_mime: String,
}
impl Header {
pub fn type_dependencies(&self) -> HashSet<Uuid> {
let mut deps = HashSet::new();
for export in &self.exports {
match export {
ExportSymbol::Function(function) => {
deps = deps
.union(&function.ret.type_dependencies())
.cloned()
.collect();
for parameter in &function.parameters {
deps = deps
.union(¶meter.ty.type_dependencies())
.cloned()
.collect();
}
}
}
}
for import in &self.imports {
match import {
ImportSymbol::Function(function) => {
deps = deps
.union(&function.ret.type_dependencies())
.cloned()
.collect();
for parameter in &function.parameters {
deps = deps
.union(¶meter.ty.type_dependencies())
.cloned()
.collect();
}
}
}
}
deps
}
pub fn module_dependencies(&self) -> HashSet<Uuid> {
let mut deps = HashSet::new();
for import in &self.imports {
deps.insert(*import.module());
}
deps
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ModuleDefinition {
pub schema_version: u32,
pub header: Header,
pub executable: Box<[u8]>,
}