use crate::types::{self, *}; use clang::{EntityKind, Usr}; use serde_json::{Map, Value as JSONValue}; use thiserror::Error; use std::collections::BTreeMap; use std::convert::TryFrom; // TODO: factor out hardcoded strings #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum ClangEntityKind { Namespace, Variable(VariableKind), Struct(StructKind), Function(FunctionKind), Typedef, Enum, EnumConstant, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum VariableKind { Variable, Field, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum StructKind { Struct, Class, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum FunctionKind { Function, Method, } impl ClangEntityKind { fn to_str_singular(self) -> &'static str { match self { ClangEntityKind::Namespace => "namespace", ClangEntityKind::Variable(VariableKind::Variable) => "variable", ClangEntityKind::Variable(VariableKind::Field) => "field", ClangEntityKind::Struct(StructKind::Struct) => "struct", ClangEntityKind::Struct(StructKind::Class) => "class", ClangEntityKind::Function(FunctionKind::Function) => "function", ClangEntityKind::Function(FunctionKind::Method) => "method", ClangEntityKind::Typedef => "typedef", ClangEntityKind::Enum => "enum", ClangEntityKind::EnumConstant => "enum-constant", } } fn to_str_plural(self) -> &'static str { match self { ClangEntityKind::Namespace => "namespaces", ClangEntityKind::Variable(VariableKind::Variable) => "variables", ClangEntityKind::Variable(VariableKind::Field) => "fields", ClangEntityKind::Struct(StructKind::Struct) => "structs", ClangEntityKind::Struct(StructKind::Class) => "classes", ClangEntityKind::Function(FunctionKind::Function) => "functions", ClangEntityKind::Function(FunctionKind::Method) => "methods", ClangEntityKind::Typedef => "typedefs", ClangEntityKind::Enum => "enums", ClangEntityKind::EnumConstant => "enum-constants", } } } #[derive(Debug, Error)] #[error("Unsupported Clang entity kind: {:?}", _0)] pub(super) struct TryFromEntityKindError(String); impl TryFrom for ClangEntityKind { type Error = TryFromEntityKindError; fn try_from(kind: EntityKind) -> Result { Ok(match kind { EntityKind::Namespace => ClangEntityKind::Namespace, EntityKind::VarDecl => ClangEntityKind::Variable(VariableKind::Variable), EntityKind::FieldDecl => ClangEntityKind::Variable(VariableKind::Field), EntityKind::FunctionDecl | EntityKind::FunctionTemplate => { ClangEntityKind::Function(FunctionKind::Function) } EntityKind::Method | EntityKind::Constructor | EntityKind::Destructor => { ClangEntityKind::Function(FunctionKind::Method) } EntityKind::StructDecl => ClangEntityKind::Struct(StructKind::Struct), EntityKind::ClassDecl | EntityKind::ClassTemplate => { ClangEntityKind::Struct(StructKind::Class) } EntityKind::TypedefDecl | EntityKind::TypeAliasDecl | EntityKind::TypeAliasTemplateDecl => ClangEntityKind::Typedef, EntityKind::EnumDecl => ClangEntityKind::Enum, EntityKind::EnumConstantDecl => ClangEntityKind::EnumConstant, kind => return Err(TryFromEntityKindError(format!("{:?}", kind))), }) } } #[derive(Debug)] pub(super) struct PartialEntity { properties: Map, children: Children, } pub(super) trait ClangEntity { fn kind(&self) -> ClangEntityKind; fn get_member_namespaces(&mut self) -> Option<&mut BTreeMap>> { None } fn get_member_variables(&mut self) -> Option<&mut BTreeMap>> { None } fn get_member_structs(&mut self) -> Option<&mut BTreeMap>> { None } fn get_member_functions(&mut self) -> Option<&mut BTreeMap>> { None } fn get_member_typedefs(&mut self) -> Option<&mut BTreeMap>> { None } fn get_member_enums(&mut self) -> Option<&mut BTreeMap>> { None } } /* pub(super) trait FromNamespaceParent: ClangEntity + Sized { fn from_namespace_parent<'a>( parent: &'a mut Described, name: &Usr, ) -> Option<&'a mut Described>; } */ pub(super) trait NamespaceParentManipulation { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>>; } impl NamespaceParentManipulation for U where U: ClangEntity, { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { self.get_member_namespaces() } } impl NamespaceParentManipulation for U where U: ClangEntity, { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { self.get_member_variables() } } impl NamespaceParentManipulation for U where U: ClangEntity, { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { self.get_member_functions() } } impl NamespaceParentManipulation for U where U: ClangEntity, { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { self.get_member_structs() } } impl NamespaceParentManipulation for U where U: ClangEntity, { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { self.get_member_typedefs() } } impl NamespaceParentManipulation for U where U: ClangEntity, { fn get_members_mut(&mut self) -> Option<&mut BTreeMap>> { self.get_member_enums() } } fn entity_id(usr: Usr) -> EntityId { // This is a somewhat ugly workaround, because Pandoc parses markdown identifiers as alphanum // or one of "-_:.", which means Pandoc can't parse libclang's USRs in title ids and related. // // EntityId(usr.0.replace("@", "::").replace("#", ".").replace("$", "-")) } fn append_children>( acc: &mut types::Children, to_insert: BTreeMap>, ) { for (usr, child) in to_insert { acc.entry(ChildrenKind(String::from( child.entity.kind().to_str_plural(), ))) .or_default() .insert(entity_id(usr), child.into()); } } impl> From> for Entity { fn from(entity: Described) -> Self { let kind = EntityKind(String::from(entity.entity.kind().to_str_singular())); let partial_entity: PartialEntity = entity.entity.into(); Entity { name: entity.description.name, language: Language::Clang, kind, brief_description: entity.description.brief, documentation: entity.description.detailed, properties: partial_entity.properties, children: partial_entity.children, } } } #[derive(Debug, Clone, Default)] pub(super) struct Description { pub(super) name: String, pub(super) brief: String, pub(super) detailed: String, } #[derive(Debug, Clone)] pub(super) struct Described { pub(super) description: Description, pub(super) entity: T, } #[derive(Debug, Clone)] pub(super) struct Namespace { pub(super) member_namespaces: BTreeMap>, pub(super) member_variables: BTreeMap>, pub(super) member_structs: BTreeMap>, pub(super) member_functions: BTreeMap>, pub(super) member_typedefs: BTreeMap>, pub(super) member_enums: BTreeMap>, } impl ClangEntity for Namespace { fn kind(&self) -> ClangEntityKind { ClangEntityKind::Namespace } fn get_member_namespaces(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_namespaces) } fn get_member_variables(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_variables) } fn get_member_structs(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_structs) } fn get_member_functions(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_functions) } fn get_member_typedefs(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_typedefs) } fn get_member_enums(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_enums) } } /* impl FromNamespaceParent for Namespace { fn from_namespace_parent<'a>( parent: &'a mut Described, name: &Usr, ) -> Option<&'a mut Described> { parent.entity.member_namespaces.get_mut(name) } } */ impl From for PartialEntity { fn from(entity: Namespace) -> Self { let mut children = Default::default(); append_children(&mut children, entity.member_namespaces); append_children(&mut children, entity.member_variables); append_children(&mut children, entity.member_structs); append_children(&mut children, entity.member_functions); append_children(&mut children, entity.member_typedefs); append_children(&mut children, entity.member_enums); PartialEntity { properties: Default::default(), children, } } } #[derive(Debug, Clone)] pub(super) struct Variable { pub(super) kind: VariableKind, pub(super) r#type: String, } impl ClangEntity for Variable { fn kind(&self) -> ClangEntityKind { ClangEntityKind::Variable(self.kind) } } impl From for PartialEntity { fn from(entity: Variable) -> Self { let mut properties = Map::new(); properties.insert(String::from("type"), JSONValue::String(entity.r#type)); PartialEntity { properties, children: Default::default(), } } } /* impl FromNamespaceParent for Variable { fn from_namespace_parent<'a>( parent: &'a mut Described, name: &Usr, ) -> Option<&'a mut Described> { parent.entity.member_variables.get_mut(name) } } */ #[derive(Debug, Clone)] pub(super) struct Struct { //pub(super) member_types: Vec, pub(super) kind: StructKind, pub(super) member_variables: BTreeMap>, pub(super) member_structs: BTreeMap>, pub(super) member_functions: BTreeMap>, pub(super) member_typedefs: BTreeMap>, pub(super) member_enums: BTreeMap>, } impl ClangEntity for Struct { fn kind(&self) -> ClangEntityKind { ClangEntityKind::Struct(self.kind) } fn get_member_variables(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_variables) } fn get_member_structs(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_structs) } fn get_member_functions(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_functions) } fn get_member_typedefs(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_typedefs) } fn get_member_enums(&mut self) -> Option<&mut BTreeMap>> { Some(&mut self.member_enums) } } impl From for PartialEntity { fn from(entity: Struct) -> Self { let mut children = Default::default(); append_children(&mut children, entity.member_variables); append_children(&mut children, entity.member_structs); append_children(&mut children, entity.member_functions); append_children(&mut children, entity.member_typedefs); append_children(&mut children, entity.member_enums); PartialEntity { properties: Default::default(), children, } } } /* impl FromNamespaceParent for Struct { fn from_namespace_parent<'a>( parent: &'a mut Described, name: &Usr, ) -> Option<&'a mut Described> { parent.entity.member_structs.get_mut(name) } } */ #[derive(Debug, Clone)] pub(super) struct Function { pub(super) kind: FunctionKind, pub(super) arguments: Vec, pub(super) return_type: String, } impl ClangEntity for Function { fn kind(&self) -> ClangEntityKind { ClangEntityKind::Function(self.kind) } } impl From for PartialEntity { fn from(entity: Function) -> Self { let mut properties = Map::with_capacity(2); properties.insert( String::from("return-type"), JSONValue::String(entity.return_type), ); properties.insert( String::from("arguments"), JSONValue::Array(entity.arguments.into_iter().map(Into::into).collect()), ); PartialEntity { properties, children: Default::default(), } } } /* impl FromNamespaceParent for Function { fn from_namespace_parent<'a>( parent: &'a mut Described, name: &Usr, ) -> Option<&'a mut Described> { parent.entity.member_functions.get_mut(name) } } */ #[derive(Debug, Clone)] pub(super) struct FunctionArgument { pub(super) name: String, pub(super) r#type: String, } impl From for JSONValue { fn from(argument: FunctionArgument) -> JSONValue { let mut object = Map::new(); object.insert(String::from("type"), JSONValue::String(argument.r#type)); object.insert(String::from("name"), JSONValue::String(argument.name)); JSONValue::Object(object) } } #[derive(Debug, Clone)] pub(super) struct Typedef { pub(super) referee: String, } impl ClangEntity for Typedef { fn kind(&self) -> ClangEntityKind { ClangEntityKind::Typedef } } impl From for PartialEntity { fn from(entity: Typedef) -> Self { let mut properties = Map::with_capacity(1); properties.insert(String::from("referee"), JSONValue::String(entity.referee)); PartialEntity { properties, children: Default::default(), } } } #[derive(Debug, Clone)] pub(super) struct Enum { pub(super) underlying_type: String, pub(super) constants: BTreeMap>, } impl ClangEntity for Enum { fn kind(&self) -> ClangEntityKind { ClangEntityKind::Enum } } impl From for PartialEntity { fn from(entity: Enum) -> Self { let mut properties = Map::with_capacity(1); properties.insert( String::from("underlying-type"), JSONValue::String(entity.underlying_type), ); let mut children = Default::default(); append_children(&mut children, entity.constants); PartialEntity { properties, children, } } } #[derive(Debug, Clone)] pub(super) struct EnumConstant { pub(super) value: String, } impl ClangEntity for EnumConstant { fn kind(&self) -> ClangEntityKind { ClangEntityKind::EnumConstant } } impl From for PartialEntity { fn from(entity: EnumConstant) -> Self { let mut properties = Map::with_capacity(1); properties.insert(String::from("value"), JSONValue::String(entity.value)); PartialEntity { properties, children: Default::default(), } } }