mod builder;
mod helpers;
use crate::Result;
use helpers::*;
use std::{collections::HashMap, path::PathBuf};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ParameterAttribute {
None,
Opt,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type {
Option(String),
Named(String),
Unit,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Method {
pub has_self: bool,
pub name: String,
pub self_type: String,
pub parameters: Vec<(String, Type, ParameterAttribute)>,
pub return_type: Type,
pub documentation: String,
pub file: PathBuf,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Property {
pub name: String,
pub typ: Type,
pub documentation: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct GdnativeClass {
pub name: String,
pub inherit: String,
pub documentation: String,
pub properties: Vec<Property>,
pub methods: Vec<Method>,
pub file: PathBuf,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Documentation {
pub name: String,
pub root_file: PathBuf,
pub root_documentation: String,
pub classes: HashMap<String, GdnativeClass>,
}
impl Documentation {
pub(crate) fn from_root_file(name: String, root_file: PathBuf) -> Result<Self> {
use syn::visit::Visit;
let root_file_content = read_file_at(&root_file)?;
let mut builder = builder::DocumentationBuilder {
documentation: Self {
name,
root_file: root_file.clone(),
root_documentation: String::new(),
classes: HashMap::new(),
},
current_file: (root_file, true),
current_module: Vec::new(),
error: None,
};
let root_documentation = get_docs(&root_file_content.attrs);
for item in root_file_content.items {
builder.visit_item(&item);
if let Some(error) = builder.error.take() {
return Err(error);
}
}
builder.documentation.root_documentation = root_documentation;
Ok(builder.documentation)
}
}
impl GdnativeClass {
fn add_method(&mut self, method: &syn::ImplItemMethod, file: PathBuf) {
let syn::ImplItemMethod {
vis, attrs, sig, ..
} = method;
if !matches!(vis, syn::Visibility::Public(_)) {
return;
}
if !(attributes_contains(attrs, "export") || sig.ident == "new") {
return;
}
let has_self = sig.receiver().is_some();
let syn::Signature {
ident: method_name,
inputs,
output,
..
} = sig;
let mut parameters = inputs.into_iter();
if has_self {
parameters.next();
}
parameters.next();
let parameters = {
let mut params = Vec::new();
for arg in parameters {
if let syn::FnArg::Typed(syn::PatType { attrs, pat, ty, .. }) = arg {
let arg_name = {
if let syn::Pat::Ident(syn::PatIdent { ident, .. }) = pat.as_ref() {
ident.to_string()
} else {
String::new()
}
};
params.push((
arg_name,
get_type_name(*ty.clone())
.unwrap_or_else(|| Type::Named("{ERROR}".to_string())),
if attributes_contains(&attrs, "opt") {
ParameterAttribute::Opt
} else {
ParameterAttribute::None
},
))
}
}
params
};
let return_type = match output {
syn::ReturnType::Default => Type::Unit,
syn::ReturnType::Type(_, typ) => get_type_name(*typ.clone()).unwrap_or(Type::Unit),
};
log::trace!(
"added method {}: parameters = {:?}, return = {:?}",
method_name,
parameters,
return_type
);
self.methods.push(Method {
has_self,
name: method_name.to_string(),
self_type: self.name.clone(),
parameters,
return_type,
documentation: get_docs(&attrs),
file,
})
}
fn get_properties(&mut self, fields: &syn::FieldsNamed) {
for field in &fields.named {
if attributes_contains(&field.attrs, "property") {
let property = Property {
name: field
.ident
.as_ref()
.map(|ident| ident.to_string())
.unwrap_or_default(),
typ: get_type_name(field.ty.clone()).unwrap_or(Type::Unit),
documentation: get_docs(&field.attrs),
};
log::trace!(
"added property '{}' of type {:?}",
property.name,
property.typ
);
self.properties.push(property)
}
}
}
}