Rewrite stab generation to be more extensible
This commit is contained in:
parent
ef811ecfa9
commit
c760d12c39
@ -101,7 +101,7 @@ fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
||||
|
||||
let ffi = ffi_crate();
|
||||
let ty = &str.ident;
|
||||
let build = generate_cdef_build(&get_cfields(&mut str.fields)?)?;
|
||||
let build = generate_cdef_build(&parse_cfields(&mut str.fields)?)?;
|
||||
|
||||
Ok(quote!(
|
||||
unsafe impl #ffi::Cdef for #ty {
|
||||
@ -123,7 +123,7 @@ fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
||||
.variants
|
||||
.iter_mut()
|
||||
.map(|variant| {
|
||||
let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?;
|
||||
let build = generate_cdef_build(&parse_cfields(&mut variant.fields)?)?;
|
||||
Ok(quote!(b.inner_struct(|b| { #build })))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
@ -148,7 +148,7 @@ struct CFieldAttrs {
|
||||
opaque: bool,
|
||||
}
|
||||
|
||||
fn get_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
||||
fn parse_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
||||
match fields {
|
||||
Fields::Named(fields) => fields.named.iter_mut(),
|
||||
Fields::Unnamed(fields) => fields.unnamed.iter_mut(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::utils::{
|
||||
StringLike, ffi_crate, is_optionlike, is_primitivelike, is_stringlike, is_unit, pat_ident,
|
||||
syn_assert, syn_error, ty_name,
|
||||
StringLike, ffi_crate, is_optionlike, is_primitivelike, is_resultlike, is_stringlike, is_unit,
|
||||
pat_ident, syn_assert, syn_error, ty_name,
|
||||
};
|
||||
use darling::FromMeta;
|
||||
use proc_macro2::TokenStream;
|
||||
@ -78,7 +78,7 @@ fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
});
|
||||
|
||||
// process extern "Lua-C" ffi functions
|
||||
for func in get_ffi_functions(imp)? {
|
||||
for func in parse_ffi_functions(imp)? {
|
||||
if let Some(mm) = func.attrs.metamethod {
|
||||
syn_assert!(
|
||||
mms.insert(mm),
|
||||
@ -91,7 +91,7 @@ fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
}
|
||||
|
||||
// process extern "Lua" lua functions
|
||||
for func in get_lua_functions(imp)? {
|
||||
for func in parse_lua_functions(imp)? {
|
||||
if let Some(mm) = func.attrs.metamethod {
|
||||
syn_assert!(
|
||||
mms.insert(mm),
|
||||
@ -230,8 +230,8 @@ struct FfiFunctionAttrs {
|
||||
metamethod: Option<Metamethod>,
|
||||
}
|
||||
|
||||
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
let mut funcs = vec![];
|
||||
fn parse_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
let mut parsed = vec![];
|
||||
|
||||
for item in imp.items.iter_mut() {
|
||||
if let ImplItem::Fn(func) = item
|
||||
@ -284,22 +284,19 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
}
|
||||
}
|
||||
|
||||
let attrs = parse_ffi_function_attrs(&mut func.attrs)?;
|
||||
attrs.metamethod.map(|mm| document_metamethod(func, mm));
|
||||
func.sig.asyncness.is_some().then(|| document_async(func));
|
||||
document_ffi_function(func);
|
||||
|
||||
funcs.push(FfiFunction {
|
||||
parsed.push(FfiFunction {
|
||||
name: func.sig.ident.clone(),
|
||||
params,
|
||||
ret,
|
||||
attrs,
|
||||
attrs: parse_ffi_function_attrs(&mut func.attrs)?,
|
||||
is_async: func.sig.asyncness.is_some(),
|
||||
});
|
||||
|
||||
document_ffi_function(func, parsed.last().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(funcs)
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAttrs> {
|
||||
@ -547,8 +544,8 @@ struct LuaFunctionAttrs {
|
||||
metamethod: Option<Metamethod>,
|
||||
}
|
||||
|
||||
fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
let mut funcs = vec![];
|
||||
fn parse_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
let mut parsed = vec![];
|
||||
|
||||
for item in imp.items.iter_mut() {
|
||||
if let ImplItem::Fn(func) = item
|
||||
@ -582,23 +579,36 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
params.push(parse_quote_spanned!(variadic.span() => variadic!())); // luaify builtin macro
|
||||
}
|
||||
|
||||
let attrs = parse_lua_function_attrs(&mut func.attrs)?;
|
||||
attrs.metamethod.map(|mm| document_metamethod(func, mm));
|
||||
func.sig.asyncness.is_some().then(|| document_async(func));
|
||||
document_lua_function(func);
|
||||
|
||||
funcs.push(LuaFunction {
|
||||
parsed.push(LuaFunction {
|
||||
name: func.sig.ident.clone(),
|
||||
params,
|
||||
body: func.block.clone(),
|
||||
attrs,
|
||||
attrs: parse_lua_function_attrs(&mut func.attrs)?,
|
||||
});
|
||||
|
||||
document_lua_function(func, parsed.last().unwrap());
|
||||
stub_lua_function(func)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(funcs)
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAttrs> {
|
||||
let mut parsed = LuaFunctionAttrs::default();
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident()
|
||||
&& let Ok(method) = Metamethod::try_from(&name.unraw())
|
||||
{
|
||||
parsed.metamethod = Some(method);
|
||||
attrs.remove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
fn stub_lua_function(func: &mut ImplItemFn) -> Result<()> {
|
||||
@ -649,23 +659,6 @@ fn stub_lua_function(func: &mut ImplItemFn) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAttrs> {
|
||||
let mut parsed = LuaFunctionAttrs::default();
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident()
|
||||
&& let Ok(method) = Metamethod::try_from(&name.unraw())
|
||||
{
|
||||
parsed.metamethod = Some(method);
|
||||
attrs.remove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
fn add_lua_function(registry: &mut Registry, func: &LuaFunction) -> Result<()> {
|
||||
let ffi = ffi_crate();
|
||||
let luaify = quote!(#ffi::__internal::luaify!);
|
||||
@ -750,75 +743,176 @@ fn inject_merged_drop(registry: &mut Registry, lua: Option<&LuaFunction>) -> Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn document_ffi_function(func: &mut ImplItemFn) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span
|
||||
class="stab"
|
||||
title="This function is implemented in Rust and called via FFI."
|
||||
style="float: right; background: #fff5d6; font-weight: 500; margin-left: 3px; padding-left: 5px; padding-right: 5px;"
|
||||
>FFI</span>"#
|
||||
]));
|
||||
// the transparency makes it work in dark mode too
|
||||
// const FFI_COLOR: &str = "rgba(255, 222, 118, 0.3)"; // yellow
|
||||
// const LUA_COLOR: &str = "rgba(188, 222, 255, 0.3)"; // blue
|
||||
const FALLIBLE_COLOR: &str = "rgba(255, 168, 168, 0.3)"; // red
|
||||
const ASYNC_COLOR: &str = "rgba(188, 222, 255, 0.3)"; // blue
|
||||
const METAMETHOD_COLOR: &str = "rgba(255, 222, 118, 0.3)"; // yellow
|
||||
|
||||
struct StabConfig {
|
||||
text: &'static str,
|
||||
desc: &'static str,
|
||||
color: &'static str,
|
||||
strong: bool,
|
||||
}
|
||||
|
||||
fn document_lua_function(func: &mut ImplItemFn) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span
|
||||
class="stab"
|
||||
title="This function is implemented in Lua."
|
||||
style="float: right; background: #ebf5ff; font-weight: 500; margin-left: 3px; padding-left: 5px; padding-right: 5px;"
|
||||
>Lua</span>"#
|
||||
]));
|
||||
fn generate_stab(
|
||||
StabConfig {
|
||||
text,
|
||||
desc,
|
||||
color,
|
||||
strong,
|
||||
}: StabConfig,
|
||||
) -> Attribute {
|
||||
let attrs = vec![
|
||||
("class", "stab".into()),
|
||||
("title", desc.into()),
|
||||
("aria-label", desc.into()),
|
||||
("style", {
|
||||
let mut style = vec![
|
||||
("float", "right"),
|
||||
("margin", "1px"),
|
||||
("margin-left", "4px"),
|
||||
("padding-left", "5px"),
|
||||
("padding-right", "5px"),
|
||||
("background", color),
|
||||
];
|
||||
|
||||
if strong {
|
||||
style.push(("font-weight", "600"));
|
||||
}
|
||||
|
||||
style
|
||||
.into_iter()
|
||||
.map(|(k, v)| format!("{k}: {v}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ")
|
||||
}),
|
||||
];
|
||||
|
||||
let attrs = attrs
|
||||
.into_iter()
|
||||
.map(|(k, v)| format!(r#"{k}="{v}""#))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
|
||||
let tag = format!(r#"<span {attrs}>{text}</span>"#);
|
||||
parse_quote!(#[doc = #tag])
|
||||
}
|
||||
|
||||
fn document_async(func: &mut ImplItemFn) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span
|
||||
class="stab"
|
||||
title="This function is asynchronous and will yield the calling thread."
|
||||
style="float: right; background: #ebf5ff; margin-left: 3px; padding-left: 5px; padding-right: 5px;"
|
||||
>Async</span>"#
|
||||
]));
|
||||
fn document_ffi_function(item: &mut ImplItemFn, func: &FfiFunction) {
|
||||
let mut attrs = vec![];
|
||||
|
||||
// attrs.push(generate_stab(StabConfig {
|
||||
// text: "FFI",
|
||||
// desc: "This function is implemented in Rust and called via FFI.",
|
||||
// color: FFI_COLOR,
|
||||
// strong: true,
|
||||
// }));
|
||||
|
||||
generate_stab_fallible(item).map(|stab| attrs.push(stab));
|
||||
generate_stab_async(item).map(|stab| attrs.push(stab));
|
||||
generate_stab_metamethod(func.attrs.metamethod).map(|(stab, help)| {
|
||||
attrs.push(stab);
|
||||
item.attrs.push(help);
|
||||
});
|
||||
|
||||
item.attrs.splice(0..0, attrs);
|
||||
}
|
||||
|
||||
fn document_metamethod(func: &mut ImplItemFn, method: Metamethod) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span
|
||||
class="stab"
|
||||
title="This function is a metamethod."
|
||||
style="float: right; background: #ebf5ff; margin-left: 3px; padding-left: 5px; padding-right: 5px;"
|
||||
>Metamethod</span>"#
|
||||
]));
|
||||
fn document_lua_function(item: &mut ImplItemFn, func: &LuaFunction) {
|
||||
let mut attrs = vec![];
|
||||
|
||||
let doc = match method {
|
||||
Metamethod::Eq => "This function is a metamethod which is called by the `==` operator.",
|
||||
Metamethod::Len => "This function is a metamethod which is called by the `#` operator.",
|
||||
Metamethod::Lt => "This function is a metamethod which is called by the `<` operator.",
|
||||
Metamethod::Le => "This function is a metamethod which is called by the `<=` operator.",
|
||||
Metamethod::Concat => "This function is a metamethod which is called by the `..` operator.",
|
||||
Metamethod::Call => {
|
||||
"This function is a metamethod which can be called by calling `(...)` on the value directly."
|
||||
}
|
||||
Metamethod::Add => "This function is a metamethod which is called by the `+` operator.",
|
||||
Metamethod::Sub => "This function is a metamethod which is called by the `-` operator.",
|
||||
Metamethod::Mul => "This function is a metamethod which is called by the `*` operator.",
|
||||
Metamethod::Div => "This function is a metamethod which is called by the `/` operator.",
|
||||
Metamethod::Mod => "This function is a metamethod which is called by the `%` operator.",
|
||||
Metamethod::Pow => "This function is a metamethod which is called by the `^` operator.",
|
||||
Metamethod::Unm => "This function is a metamethod which is called by the `-` operator.",
|
||||
Metamethod::ToString => {
|
||||
"This function is a metamethod which is called by the [`tostring(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-tostring) built-in function."
|
||||
}
|
||||
Metamethod::Pairs => {
|
||||
"This function is a metamethod which is called by the [`pairs(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pairs) built-in function."
|
||||
}
|
||||
Metamethod::Ipairs => {
|
||||
"This function is a metamethod which is called by the [`ipairs(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-ipairs) built-in function."
|
||||
}
|
||||
_ => "This function is a metamethod and cannot be called directly.",
|
||||
};
|
||||
// attrs.push(generate_stab(StabConfig {
|
||||
// text: "Lua",
|
||||
// desc: "This function is implemented in Lua.",
|
||||
// color: LUA_COLOR,
|
||||
// strong: true,
|
||||
// }));
|
||||
|
||||
func.attrs.push(parse_quote!(#[doc = ""]));
|
||||
func.attrs.push(parse_quote!(#[doc = "# Metamethod"]));
|
||||
func.attrs.push(parse_quote!(#[doc = ""]));
|
||||
func.attrs.push(parse_quote!(#[doc = #doc]));
|
||||
generate_stab_fallible(item).map(|stab| attrs.push(stab));
|
||||
generate_stab_async(item).map(|stab| attrs.push(stab));
|
||||
generate_stab_metamethod(func.attrs.metamethod).map(|(stab, help)| {
|
||||
attrs.push(stab);
|
||||
item.attrs.push(help);
|
||||
});
|
||||
|
||||
item.attrs.splice(0..0, attrs);
|
||||
}
|
||||
|
||||
fn generate_stab_async(item: &ImplItemFn) -> Option<Attribute> {
|
||||
item.sig.asyncness.as_ref().map(|_| {
|
||||
generate_stab(StabConfig {
|
||||
text: "Async",
|
||||
desc: "This function is asynchronous and will yield the calling thread.",
|
||||
color: ASYNC_COLOR,
|
||||
strong: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_stab_fallible(item: &ImplItemFn) -> Option<Attribute> {
|
||||
match item.sig.output {
|
||||
ReturnType::Default => None,
|
||||
ReturnType::Type(_, ref ty) => is_resultlike(ty).map(|_| {
|
||||
generate_stab(StabConfig {
|
||||
text: "Fallible",
|
||||
desc: "This function is fallible and may throw an error on failure.",
|
||||
color: FALLIBLE_COLOR,
|
||||
strong: false,
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_stab_metamethod(method: Option<Metamethod>) -> Option<(Attribute, Attribute)> {
|
||||
method.map(|method| {
|
||||
let stab = generate_stab(StabConfig {
|
||||
text: "Metamethod",
|
||||
desc: "This function is a metamethod.",
|
||||
color: METAMETHOD_COLOR,
|
||||
strong: false,
|
||||
});
|
||||
|
||||
let help = match method {
|
||||
Metamethod::Eq => "This function is a metamethod which is called by the `==` operator.",
|
||||
Metamethod::Len => "This function is a metamethod which is called by the `#` operator.",
|
||||
Metamethod::Lt => "This function is a metamethod which is called by the `<` operator.",
|
||||
Metamethod::Le => "This function is a metamethod which is called by the `<=` operator.",
|
||||
Metamethod::Concat => {
|
||||
"This function is a metamethod which is called by the `..` operator."
|
||||
}
|
||||
Metamethod::Call => {
|
||||
"This function is a metamethod which can be called by calling \
|
||||
`(...)` on the value directly."
|
||||
}
|
||||
Metamethod::Add => "This function is a metamethod which is called by the `+` operator.",
|
||||
Metamethod::Sub => "This function is a metamethod which is called by the `-` operator.",
|
||||
Metamethod::Mul => "This function is a metamethod which is called by the `*` operator.",
|
||||
Metamethod::Div => "This function is a metamethod which is called by the `/` operator.",
|
||||
Metamethod::Mod => "This function is a metamethod which is called by the `%` operator.",
|
||||
Metamethod::Pow => "This function is a metamethod which is called by the `^` operator.",
|
||||
Metamethod::Unm => "This function is a metamethod which is called by the `-` operator.",
|
||||
Metamethod::ToString => {
|
||||
"This function is a metamethod which is called by the \
|
||||
[`tostring(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-tostring) \
|
||||
built-in function."
|
||||
}
|
||||
Metamethod::Pairs => {
|
||||
"This function is a metamethod which is called by the \
|
||||
[`pairs(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pairs) \
|
||||
built-in function."
|
||||
}
|
||||
Metamethod::Ipairs => {
|
||||
"This function is a metamethod which is called by the \
|
||||
[`ipairs(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-ipairs) \
|
||||
built-in function."
|
||||
}
|
||||
_ => "This function is a metamethod and cannot be called directly.",
|
||||
};
|
||||
|
||||
let help = format!("\n# Metamethod\n\n{help}");
|
||||
(stab, parse_quote!(#[doc = #help]))
|
||||
})
|
||||
}
|
||||
|
@ -163,3 +163,19 @@ pub fn is_optionlike(ty: &Type) -> Option<&Type> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_resultlike(ty: &Type) -> Option<&Type> {
|
||||
if let Type::Path(path) = ty
|
||||
&& path.path.leading_colon.is_none()
|
||||
&& path.path.segments.len() == 1
|
||||
&& let Some(segment) = path.path.segments.get(0)
|
||||
&& segment.ident == "Result"
|
||||
&& let PathArguments::AngleBracketed(ref angle) = segment.arguments
|
||||
&& angle.args.len() <= 2 // allow Result<T, E> or Result<T>
|
||||
&& let Some(GenericArgument::Type(ty)) = angle.args.get(0)
|
||||
{
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user