2023-01-27 00:36:06 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
use regex::Regex;
|
|
|
|
use serde_json::{Map, Value};
|
|
|
|
|
|
|
|
use full_moon::{parse as parse_luau_ast, visitors::Visitor};
|
|
|
|
|
|
|
|
mod doc;
|
|
|
|
mod tag;
|
|
|
|
mod visitor;
|
|
|
|
|
|
|
|
use self::{doc::DocsFunctionParamLink, visitor::DocumentationVisitor};
|
|
|
|
|
2023-02-15 21:36:26 +00:00
|
|
|
pub fn parse_definitions(contents: &str) -> Result<DocumentationVisitor> {
|
2023-02-11 22:37:23 +00:00
|
|
|
// TODO: Properly handle the "declare class" syntax, for now we just skip it
|
|
|
|
let mut no_declares = contents.to_string();
|
|
|
|
while let Some(dec) = no_declares.find("\ndeclare class") {
|
|
|
|
let end = no_declares.find("\nend").unwrap();
|
|
|
|
let before = &no_declares[0..dec];
|
|
|
|
let after = &no_declares[end + 4..];
|
|
|
|
no_declares = format!("{before}{after}");
|
|
|
|
}
|
2023-01-27 00:36:06 +00:00
|
|
|
let (regex, replacement) = (
|
2023-02-06 03:25:36 +00:00
|
|
|
Regex::new(r#"declare (?P<n>\w+): "#).unwrap(),
|
|
|
|
r#"export type $n = "#,
|
2023-01-27 00:36:06 +00:00
|
|
|
);
|
2023-02-11 22:37:23 +00:00
|
|
|
let defs_ast = parse_luau_ast(®ex.replace_all(&no_declares, replacement))?;
|
2023-01-27 00:36:06 +00:00
|
|
|
let mut visitor = DocumentationVisitor::new();
|
|
|
|
visitor.visit_ast(&defs_ast);
|
|
|
|
Ok(visitor)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_docs_json_from_definitions(contents: &str, namespace: &str) -> Result<Value> {
|
|
|
|
let visitor = parse_definitions(contents)?;
|
|
|
|
/*
|
|
|
|
Extract globals, functions, params, returns from the visitor
|
|
|
|
Here we will also convert the plain names into proper namespaced names according to the spec at
|
|
|
|
https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/api-docs/en-us.json
|
|
|
|
*/
|
|
|
|
let mut map = Map::new();
|
|
|
|
for (name, mut doc) in visitor.globals {
|
|
|
|
doc.keys = doc
|
|
|
|
.keys
|
|
|
|
.iter()
|
|
|
|
.map(|(key, value)| (key.clone(), format!("@{namespace}/{name}.{value}")))
|
|
|
|
.collect::<HashMap<String, String>>();
|
|
|
|
map.insert(format!("@{namespace}/{name}"), serde_json::to_value(doc)?);
|
|
|
|
}
|
|
|
|
for (name, mut doc) in visitor.functions {
|
|
|
|
doc.params = doc
|
|
|
|
.params
|
|
|
|
.iter()
|
|
|
|
.map(|param| DocsFunctionParamLink {
|
|
|
|
name: param.name.clone(),
|
|
|
|
documentation: format!(
|
|
|
|
"@{namespace}/{}.{name}/param/{}",
|
|
|
|
doc.global_name, param.documentation
|
|
|
|
),
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
doc.returns = doc
|
|
|
|
.returns
|
|
|
|
.iter()
|
|
|
|
.map(|ret| format!("@{namespace}/{}.{name}/return/{ret}", doc.global_name))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
map.insert(
|
|
|
|
format!("@{namespace}/{}.{name}", doc.global_name),
|
|
|
|
serde_json::to_value(doc)?,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
for (name, doc) in visitor.params {
|
|
|
|
map.insert(
|
|
|
|
format!(
|
|
|
|
"@{namespace}/{}.{}/param/{name}",
|
|
|
|
doc.global_name, doc.function_name
|
|
|
|
),
|
|
|
|
serde_json::to_value(doc)?,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
for (name, doc) in visitor.returns {
|
|
|
|
map.insert(
|
|
|
|
format!(
|
|
|
|
"@{namespace}/{}.{}/return/{name}",
|
|
|
|
doc.global_name, doc.function_name
|
|
|
|
),
|
|
|
|
serde_json::to_value(doc)?,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Ok(Value::Object(map))
|
|
|
|
}
|