use crate::error::Result;
use crate::graph::FileFacts;
use crate::lang::Language;
use super::{Extractor, lua::extract_lua_family};
pub struct LuauExtractor;
impl Extractor for LuauExtractor {
fn lang(&self) -> Language {
Language::Luau
}
fn extract(&self, source: &str, file: &str) -> Result<FileFacts> {
extract_lua_family(source, file, Language::Luau, crate::grammar::luau())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::types::{RefRole, SymbolKind};
fn extract(src: &str, file: &str) -> FileFacts {
LuauExtractor.extract(src, file).unwrap()
}
fn by_name(facts: &FileFacts, name: &str) -> Option<crate::graph::types::Symbol> {
facts.symbols.iter().find(|s| s.name == name).cloned()
}
#[test]
fn type_alias_is_extracted() {
let src = "type Point = { x: number, y: number }";
let facts = extract(src, "src/geo.luau");
let sym = by_name(&facts, "Point").expect("expected Point symbol");
assert_eq!(sym.kind, SymbolKind::TypeAlias);
assert_eq!(sym.id.to_scip_string(), "codegraph . . . geo/Point#");
assert_eq!(facts.lang, "luau");
}
#[test]
fn exported_type_alias_is_extracted() {
let src = "export type ID = number";
let facts = extract(src, "src/types.luau");
let sym = by_name(&facts, "ID").expect("expected ID symbol");
assert_eq!(sym.kind, SymbolKind::TypeAlias);
}
#[test]
fn typed_function_is_extracted_as_function() {
let src = "function foo(a: number): number\n return a\nend";
let facts = extract(src, "src/math.luau");
let sym = by_name(&facts, "foo").expect("expected foo symbol");
assert_eq!(sym.kind, SymbolKind::Function);
assert_eq!(sym.id.to_scip_string(), "codegraph . . . math/foo().");
assert_eq!(facts.lang, "luau");
}
#[test]
fn table_dot_method_is_extracted_as_method_under_type() {
let src = "function M.baz() end";
let facts = extract(src, "src/mod.luau");
let sym = by_name(&facts, "baz").expect("expected baz symbol");
assert_eq!(sym.kind, SymbolKind::Method);
let scip = sym.id.to_scip_string();
assert!(
scip.contains("M#") && scip.contains("baz"),
"unexpected SCIP string: {scip}"
);
}
#[test]
fn require_produces_import_reference() {
let src = "local sub = require('pkg.sub')";
let facts = extract(src, "src/util.luau");
let imp = facts
.references
.iter()
.find(|r| r.role == RefRole::Import)
.expect("expected Import ref from require");
assert_eq!(imp.name, "sub");
assert!(
imp.from_path
.as_deref()
.is_some_and(|p| p.contains("pkg.sub")),
"from_path should contain 'pkg.sub', got {:?}",
imp.from_path
);
}
#[test]
fn luau_file_path_yields_correct_namespace() {
let src = "function helper() end";
let facts = extract(src, "src/utils/helper.luau");
let sym = by_name(&facts, "helper").expect("expected helper symbol");
let scip = sym.id.to_scip_string();
assert!(
scip.contains("utils") && scip.contains("helper"),
"unexpected SCIP string: {scip}"
);
}
}