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 let mut functions = BTreeMap::new();
144 for node in program {
145 let inner = peel_node(node);
146 match inner {
147 Node::StructDecl { name, fields, .. } => {
148 let constructor = self.compile_struct_constructor(name, fields)?;
149 functions.insert(name.clone(), constructor);
150 }
151 Node::Pipeline {
152 name,
153 params,
154 body,
155 extends,
156 ..
157 } => {
158 let function = self.compile_pipeline_callable(
159 program,
160 name,
161 params,
162 body,
163 extends.as_deref(),
164 )?;
165 functions.insert(name.clone(), function);
166 }
167 Node::FnDecl {
168 name,
169 type_params,
170 params,
171 body,
172 ..
173 } => {
174 let mut compiler = Compiler::with_options(self.options);
175 compiler.prepare_module_context(program);
176 compiler.add_imported_enum_candidates(imported_enum_candidates.iter().cloned());
177 let mut function =
178 compiler.compile_fn_body(type_params, params, body, source_file.clone())?;
179 function.name = name.clone();
184 functions.insert(name.clone(), function);
185 }
186 _ => {}
187 }
188 }
189
190 Ok(CompiledPortableModule {
191 id: id.into(),
192 imports,
193 init,
194 functions,
195 exports,
196 })
197 }
198}