1use std::collections::BTreeMap;
9
10use harn_parser::{Node, SNode};
11use serde::{Deserialize, Serialize};
12
13use crate::{Chunk, CompiledFunction};
14
15use super::{peel_node, CompileError, Compiler};
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct PortableImport {
21 pub path: String,
23 pub target: String,
25 pub selected_names: Option<Vec<String>>,
26 pub namespace_alias: Option<String>,
27 pub is_pub: bool,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum PortableExportKind {
37 Function,
38 Pipeline,
39 Tool,
40 Skill,
41 EvalPack,
42 Struct,
43 Enum,
44 Interface,
45 Type,
46 Variable,
47}
48
49impl PortableExportKind {
50 pub const fn has_runtime_value(self) -> bool {
51 !matches!(self, Self::Interface | Self::Type)
52 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct PortableSourceModule {
63 pub id: String,
64 pub source: String,
65 #[serde(default)]
66 pub imports: Vec<PortableImport>,
67 #[serde(default)]
68 pub exports: BTreeMap<String, PortableExportKind>,
69 #[serde(default)]
70 pub imported_enum_candidates: Vec<String>,
71 #[serde(default)]
72 pub source_file: Option<String>,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct PortableSourcePackage {
82 pub root_source: String,
83 #[serde(default)]
84 pub root_imports: Vec<PortableImport>,
85 #[serde(default)]
86 pub modules: Vec<PortableSourceModule>,
87}
88
89#[derive(Debug, Clone)]
93pub struct CompiledPortableModule {
94 pub id: String,
95 pub imports: Vec<PortableImport>,
96 pub init: Option<Chunk>,
97 pub functions: BTreeMap<String, CompiledFunction>,
98 pub exports: BTreeMap<String, PortableExportKind>,
99}
100
101impl Compiler {
102 pub fn compile_portable_module(
106 mut self,
107 id: impl Into<String>,
108 program: &[SNode],
109 imports: Vec<PortableImport>,
110 exports: BTreeMap<String, PortableExportKind>,
111 imported_enum_candidates: &[String],
112 source_file: Option<String>,
113 ) -> Result<CompiledPortableModule, CompileError> {
114 self.prepare_module_context(program);
115 self.add_imported_enum_candidates(imported_enum_candidates.iter().cloned());
116
117 let init_nodes: Vec<SNode> = program
118 .iter()
119 .filter(|sn| {
120 let inner = peel_node(sn);
121 matches!(
122 inner,
123 Node::LetBinding { .. }
124 | Node::ConstBinding { .. }
125 | Node::EnumDecl { is_pub: true, .. }
126 | Node::ToolDecl { .. }
127 | Node::SkillDecl { .. }
128 | Node::EvalPackDecl { .. }
129 )
130 })
131 .cloned()
132 .collect();
133 let init = if init_nodes.is_empty() {
134 None
135 } else {
136 Some(Compiler::with_options(self.options).compile_module_init(
137 program,
138 &init_nodes,
139 imported_enum_candidates,
140 &[],
141 )?)
142 };
143
144 let mut functions = BTreeMap::new();
145 for node in program {
146 let inner = peel_node(node);
147 match inner {
148 Node::StructDecl { name, fields, .. } => {
149 let constructor = self.compile_struct_constructor(name, fields)?;
150 functions.insert(name.clone(), constructor);
151 }
152 Node::Pipeline {
153 name,
154 params,
155 body,
156 extends,
157 ..
158 } => {
159 let function = self.compile_pipeline_callable(
160 program,
161 name,
162 params,
163 body,
164 extends.as_deref(),
165 )?;
166 functions.insert(name.clone(), function);
167 }
168 Node::FnDecl {
169 name,
170 type_params,
171 params,
172 body,
173 ..
174 } => {
175 let mut compiler = Compiler::with_options(self.options);
176 compiler.prepare_module_context(program);
177 compiler.add_imported_enum_candidates(imported_enum_candidates.iter().cloned());
178 let mut function =
179 compiler.compile_fn_body(type_params, params, body, source_file.clone())?;
180 function.name = name.clone();
185 functions.insert(name.clone(), function);
186 }
187 _ => {}
188 }
189 }
190
191 Ok(CompiledPortableModule {
192 id: id.into(),
193 imports,
194 init,
195 functions,
196 exports,
197 })
198 }
199}