1use std::{collections::HashSet, path::PathBuf};
2
3use anyhow::{anyhow, Result};
4use full_moon::{
5 ast::{Ast, Expression, Field, LastStmt},
6 tokenizer::TokenKind,
7};
8use tokio::fs;
9
10use crate::TargetVersion;
11
12pub enum ParseTarget {
13 FullMoonAst(Ast),
14 File(PathBuf, TargetVersion),
15}
16
17pub(crate) async fn parse_file(path: &PathBuf, target_version: &TargetVersion) -> Result<Ast> {
18 let code = fs::read_to_string(path).await?;
19 let ast = full_moon::parse_fallible(code.as_str(), target_version.to_lua_version().clone())
20 .into_result()
21 .map_err(|errors| anyhow!("full_moon parsing error: {:?}", errors))?;
22
23 Ok(ast)
24}
25
26pub async fn get_exports_from_last_stmt(target: &ParseTarget) -> Result<Option<HashSet<String>>> {
28 let ast = match target {
29 ParseTarget::FullMoonAst(ast) => ast,
30 ParseTarget::File(path, target_version) => &parse_file(path, target_version).await?,
31 };
32 let block = ast.nodes();
33
34 if let Some(exports) = block
35 .last_stmt()
36 .and_then(|last_stmt| match last_stmt {
37 LastStmt::Return(return_stmt) => return_stmt.returns().first(),
38 _ => None,
39 })
40 .and_then(|first_return| match first_return.value() {
41 Expression::TableConstructor(table_constructor) => Some(table_constructor),
42 _ => None,
43 })
44 .map(|table_constructor| {
45 let mut exports: HashSet<String> = HashSet::new();
46 for field in table_constructor.fields() {
47 if let Some(new_export) = match field {
48 Field::ExpressionKey {
49 brackets: _,
50 key,
51 equal: _,
52 value: _,
53 } => {
54 if let Expression::String(string_token) = key {
55 let string_token = string_token.token();
56 if let TokenKind::StringLiteral = string_token.token_kind() {
57 log::debug!("[get_exports_from_last_stmt] ExpressionKey token kind: {:?} string: {}", string_token.token_kind(), string_token.to_string());
58 Some(string_token.to_string().trim().to_owned())
59 } else {
60 None
61 }
62 } else {
63 None
64 }
65 }
66 Field::NameKey {
67 key,
68 equal: _,
69 value: _,
70 } => {
71 let key = key.token();
72 if let TokenKind::Identifier = key.token_kind() {
73 log::debug!("[get_exports_from_last_stmt] NameKey token kind: {:?} string: {}", key.token_kind(), key.to_string());
74 Some(key.to_string().trim().to_owned())
75 } else {
76 None
77 }
78 }
79 _ => None,
80 } {
81 exports.insert(new_export);
82 }
83 }
84 exports
85 })
86 {
87 return Ok(Some(exports));
88 }
89
90 Ok(None)
91}