1use harn_parser::{BindingPattern, Node, SNode};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
5pub enum DefKind {
6 Function,
7 Pipeline,
8 Tool,
9 Skill,
10 EvalPack,
11 Struct,
12 Enum,
13 Interface,
14 Type,
15 Variable,
16 Parameter,
17}
18
19impl DefKind {
20 pub const fn has_runtime_value(self) -> bool {
24 !matches!(self, Self::Type | Self::Interface | Self::Parameter)
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct PublicDeclaration {
35 pub name: String,
36 pub kind: DefKind,
37}
38
39pub fn public_declarations(snode: &SNode) -> Vec<PublicDeclaration> {
45 match &snode.node {
46 Node::AttributedDecl { inner, .. } => public_declarations(inner),
47 Node::FnDecl {
48 name, is_pub: true, ..
49 } => declaration(name, DefKind::Function),
50 Node::Pipeline {
51 name, is_pub: true, ..
52 } => declaration(name, DefKind::Pipeline),
53 Node::ToolDecl {
54 name, is_pub: true, ..
55 } => declaration(name, DefKind::Tool),
56 Node::SkillDecl {
57 name, is_pub: true, ..
58 } => declaration(name, DefKind::Skill),
59 Node::EvalPackDecl {
60 binding_name,
61 is_pub: true,
62 ..
63 } => declaration(binding_name, DefKind::EvalPack),
64 Node::StructDecl {
65 name, is_pub: true, ..
66 } => declaration(name, DefKind::Struct),
67 Node::EnumDecl {
68 name, is_pub: true, ..
69 } => declaration(name, DefKind::Enum),
70 Node::InterfaceDecl { name, .. } => declaration(name, DefKind::Interface),
71 Node::TypeDecl {
72 name, is_pub: true, ..
73 } => declaration(name, DefKind::Type),
74 Node::LetBinding {
75 pattern,
76 is_pub: true,
77 ..
78 }
79 | Node::ConstBinding {
80 pattern,
81 is_pub: true,
82 ..
83 } => pattern_names(pattern)
84 .into_iter()
85 .map(|name| PublicDeclaration {
86 name,
87 kind: DefKind::Variable,
88 })
89 .collect(),
90 _ => Vec::new(),
91 }
92}
93
94fn declaration(name: &str, kind: DefKind) -> Vec<PublicDeclaration> {
95 vec![PublicDeclaration {
96 name: name.to_string(),
97 kind,
98 }]
99}
100
101pub(crate) fn pattern_names(pattern: &BindingPattern) -> Vec<String> {
102 match pattern {
103 BindingPattern::Identifier(name) => vec![name.clone()],
104 BindingPattern::Dict(fields) => fields
105 .iter()
106 .filter_map(|field| field.alias.as_ref().or(Some(&field.key)).cloned())
107 .collect(),
108 BindingPattern::List(elements) => elements
109 .iter()
110 .map(|element| element.name.clone())
111 .collect(),
112 BindingPattern::Pair(a, b) => vec![a.clone(), b.clone()],
113 }
114}
115
116use std::path::Path;
126
127use harn_lexer::Span;
128
129use crate::{import_recording, DefSite, ModuleInfo, PackageSnapshot};
130
131pub(crate) fn collect_module_info(
132 file: &Path,
133 snode: &SNode,
134 module: &mut ModuleInfo,
135 package_snapshots: &[PackageSnapshot],
136) {
137 if let Node::AttributedDecl { inner, .. } = &snode.node {
138 collect_module_info(file, inner, module, package_snapshots);
139 return;
140 }
141
142 for public in public_declarations(snode) {
143 module.own_exports.insert(public.name);
144 }
145
146 match &snode.node {
147 Node::FnDecl { name, params, .. } => {
148 module.declarations.insert(
149 name.clone(),
150 decl_site(file, snode.span, name, DefKind::Function),
151 );
152 for param_name in params.iter().map(|param| param.name.clone()) {
153 module.declarations.insert(
154 param_name.clone(),
155 decl_site(file, snode.span, ¶m_name, DefKind::Parameter),
156 );
157 }
158 }
159 Node::Pipeline { name, .. } => {
160 module.declarations.insert(
161 name.clone(),
162 decl_site(file, snode.span, name, DefKind::Pipeline),
163 );
164 }
165 Node::ToolDecl { name, .. } => {
166 module.declarations.insert(
167 name.clone(),
168 decl_site(file, snode.span, name, DefKind::Tool),
169 );
170 }
171 Node::SkillDecl { name, .. } => {
172 module.declarations.insert(
173 name.clone(),
174 decl_site(file, snode.span, name, DefKind::Skill),
175 );
176 }
177 Node::EvalPackDecl { binding_name, .. } => {
178 module.declarations.insert(
179 binding_name.clone(),
180 decl_site(file, snode.span, binding_name, DefKind::EvalPack),
181 );
182 }
183 Node::StructDecl { name, .. } => {
184 module.declarations.insert(
185 name.clone(),
186 decl_site(file, snode.span, name, DefKind::Struct),
187 );
188 }
189 Node::EnumDecl { name, .. } => {
190 module.declarations.insert(
191 name.clone(),
192 decl_site(file, snode.span, name, DefKind::Enum),
193 );
194 }
195 Node::InterfaceDecl { name, .. } => {
196 module.declarations.insert(
197 name.clone(),
198 decl_site(file, snode.span, name, DefKind::Interface),
199 );
200 }
201 Node::TypeDecl { name, .. } => {
202 module.declarations.insert(
203 name.clone(),
204 decl_site(file, snode.span, name, DefKind::Type),
205 );
206 }
207 Node::LetBinding { pattern, .. } | Node::ConstBinding { pattern, .. } => {
208 for name in pattern_names(pattern) {
209 module.declarations.insert(
210 name.clone(),
211 decl_site(file, snode.span, &name, DefKind::Variable),
212 );
213 }
214 }
215 _ if import_recording::record_import_node(module, file, snode, package_snapshots) => {}
216 _ => {}
217 }
218}
219
220pub(crate) fn collect_type_declarations(snode: &SNode, decls: &mut Vec<SNode>) {
221 match &snode.node {
222 Node::TypeDecl { .. }
223 | Node::StructDecl { .. }
224 | Node::EnumDecl { .. }
225 | Node::InterfaceDecl { .. } => decls.push(snode.clone()),
226 Node::AttributedDecl { inner, .. } => collect_type_declarations(inner, decls),
227 _ => {}
228 }
229}
230
231pub(crate) fn collect_callable_declarations(snode: &SNode, decls: &mut Vec<SNode>) {
232 match &snode.node {
233 Node::FnDecl { .. } | Node::Pipeline { .. } | Node::ToolDecl { .. } => {
234 decls.push(snode.clone());
235 }
236 Node::AttributedDecl { inner, .. } => collect_callable_declarations(inner, decls),
237 _ => {}
238 }
239}
240
241pub(crate) fn type_decl_name(snode: &SNode) -> Option<&str> {
242 match &snode.node {
243 Node::TypeDecl { name, .. }
244 | Node::StructDecl { name, .. }
245 | Node::EnumDecl { name, .. }
246 | Node::InterfaceDecl { name, .. } => Some(name.as_str()),
247 _ => None,
248 }
249}
250
251pub(crate) fn callable_decl_name(snode: &SNode) -> Option<&str> {
252 match &snode.node {
253 Node::FnDecl { name, .. } | Node::Pipeline { name, .. } | Node::ToolDecl { name, .. } => {
254 Some(name.as_str())
255 }
256 Node::AttributedDecl { inner, .. } => callable_decl_name(inner),
257 _ => None,
258 }
259}
260
261pub(crate) fn decl_site(file: &Path, span: Span, name: &str, kind: DefKind) -> DefSite {
262 DefSite {
263 name: name.to_string(),
264 file: file.to_path_buf(),
265 kind,
266 span,
267 }
268}