dalbit_core/
utils.rs

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
26/// Gets exports of lua modules by parsing last statement's table constructor.
27pub 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}