use libc::c_char;
use rubydex::model::declaration::{Ancestor, Declaration, Namespace};
use std::ffi::CString;
use std::ptr;
use crate::definition_api::{DefinitionsIter, rdx_definitions_iter_new_from_ids};
use crate::graph_api::{GraphPointer, with_graph};
use crate::reference_api::{CConstantReference, CMethodReference, ConstantReferencesIter, MethodReferencesIter};
use crate::utils;
use rubydex::model::ids::{DeclarationId, StringId};
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum CDeclarationKind {
Class = 0,
Module = 1,
SingletonClass = 2,
Constant = 3,
ConstantAlias = 4,
Method = 5,
GlobalVariable = 6,
InstanceVariable = 7,
ClassVariable = 8,
Todo = 9,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CDeclaration {
id: u64,
kind: CDeclarationKind,
}
impl CDeclaration {
#[must_use]
pub fn id(&self) -> u64 {
self.id
}
#[must_use]
pub fn from_declaration(id: DeclarationId, decl: &Declaration) -> Self {
Self {
id: *id,
kind: Self::kind_from_declaration(decl),
}
}
#[must_use]
pub fn kind_from_declaration(decl: &Declaration) -> CDeclarationKind {
match decl {
Declaration::Namespace(Namespace::Class(_)) => CDeclarationKind::Class,
Declaration::Namespace(Namespace::Module(_)) => CDeclarationKind::Module,
Declaration::Namespace(Namespace::SingletonClass(_)) => CDeclarationKind::SingletonClass,
Declaration::Namespace(Namespace::Todo(_)) => CDeclarationKind::Todo,
Declaration::Constant(_) => CDeclarationKind::Constant,
Declaration::ConstantAlias(_) => CDeclarationKind::ConstantAlias,
Declaration::Method(_) => CDeclarationKind::Method,
Declaration::GlobalVariable(_) => CDeclarationKind::GlobalVariable,
Declaration::InstanceVariable(_) => CDeclarationKind::InstanceVariable,
Declaration::ClassVariable(_) => CDeclarationKind::ClassVariable,
}
}
}
pub(crate) unsafe fn decl_id_from_char_ptr(ptr: *const c_char) -> Option<DeclarationId> {
if ptr.is_null() {
return None;
}
let s = unsafe { utils::convert_char_ptr_to_string(ptr) }.ok()?;
if s.is_empty() {
return None;
}
Some(DeclarationId::from(s.as_str()))
}
#[derive(Debug)]
pub struct DeclarationsIter {
entries: Box<[CDeclaration]>,
index: usize,
}
iterator!(DeclarationsIter, entries: CDeclaration);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_graph_declarations_iter_len(iter: *const DeclarationsIter) -> usize {
unsafe { DeclarationsIter::len(iter) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_graph_declarations_iter_next(iter: *mut DeclarationsIter, out: *mut CDeclaration) -> bool {
unsafe { DeclarationsIter::next(iter, out) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_graph_declarations_iter_free(iter: *mut DeclarationsIter) {
unsafe { DeclarationsIter::free(iter) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_name(pointer: GraphPointer, name_id: u64) -> *const c_char {
with_graph(pointer, |graph| {
let name_id = DeclarationId::new(name_id);
if let Some(decl) = graph.declarations().get(&name_id) {
CString::new(decl.name()).unwrap().into_raw().cast_const()
} else {
ptr::null()
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_member(
pointer: GraphPointer,
name_id: u64,
member: *const c_char,
) -> *const CDeclaration {
let Ok(member_str) = (unsafe { utils::convert_char_ptr_to_string(member) }) else {
return ptr::null();
};
with_graph(pointer, |graph| {
let name_id = DeclarationId::new(name_id);
if let Some(Declaration::Namespace(decl)) = graph.declarations().get(&name_id) {
let member_id = StringId::from(member_str.as_str());
if let Some(member_decl_id) = decl.member(&member_id) {
let member_decl = graph.declarations().get(member_decl_id).unwrap();
return Box::into_raw(Box::new(CDeclaration::from_declaration(*member_decl_id, member_decl)))
.cast_const();
}
}
ptr::null()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_find_member(
pointer: GraphPointer,
declaration_id: u64,
member: *const c_char,
only_inherited: bool,
) -> *const CDeclaration {
let Ok(member_str) = (unsafe { utils::convert_char_ptr_to_string(member) }) else {
return ptr::null();
};
with_graph(pointer, |graph| {
let id = DeclarationId::new(declaration_id);
let member_id = StringId::from(member_str.as_str());
let member_decl_id = match rubydex::query::find_member_in_ancestors(graph, id, member_id, only_inherited) {
Ok(decl_id) => decl_id,
Err(rubydex::query::FindMemberError::MemberNotFound) => return ptr::null(),
Err(err) => unreachable!(
"Namespace#find_member is only exposed on namespace declarations, so the declaration must exist and be \
a namespace, got {err:?}"
),
};
let member_decl = graph.declarations().get(&member_decl_id).unwrap();
Box::into_raw(Box::new(CDeclaration::from_declaration(member_decl_id, member_decl))).cast_const()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_unqualified_name(pointer: GraphPointer, name_id: u64) -> *const c_char {
with_graph(pointer, |graph| {
let name_id = DeclarationId::new(name_id);
if let Some(decl) = graph.declarations().get(&name_id) {
CString::new(decl.unqualified_name()).unwrap().into_raw().cast_const()
} else {
ptr::null()
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_definitions_iter_new(
pointer: GraphPointer,
decl_id: u64,
) -> *mut DefinitionsIter {
with_graph(pointer, |graph| {
let decl_id = DeclarationId::new(decl_id);
if let Some(decl) = graph.declarations().get(&decl_id) {
rdx_definitions_iter_new_from_ids(graph, decl.definitions())
} else {
DefinitionsIter::new(Vec::<_>::new().into_boxed_slice())
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_singleton_class(pointer: GraphPointer, decl_id: u64) -> *const CDeclaration {
with_graph(pointer, |graph| {
let declaration = graph
.declarations()
.get(&DeclarationId::new(decl_id))
.unwrap()
.as_namespace()
.unwrap();
if let Some(singleton_id) = declaration.singleton_class() {
Box::into_raw(Box::new(CDeclaration::from_declaration(
*singleton_id,
graph.declarations().get(singleton_id).unwrap(),
)))
} else {
ptr::null()
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_owner(pointer: GraphPointer, decl_id: u64) -> *const CDeclaration {
with_graph(pointer, |graph| {
let declaration = graph.declarations().get(&DeclarationId::new(decl_id)).unwrap();
let owner_id = *declaration.owner_id();
Box::into_raw(Box::new(CDeclaration::from_declaration(
owner_id,
graph.declarations().get(&owner_id).unwrap(),
)))
.cast_const()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_c_declaration(ptr: *const CDeclaration) {
if ptr.is_null() {
return;
}
unsafe {
let _ = Box::from_raw(ptr.cast_mut());
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_ancestors(pointer: GraphPointer, decl_id: u64) -> *mut DeclarationsIter {
let declarations = with_graph(pointer, |graph| {
let declaration_id = DeclarationId::new(decl_id);
let Some(Declaration::Namespace(declaration)) = graph.declarations().get(&declaration_id) else {
return Vec::new();
};
declaration
.ancestors()
.into_iter()
.filter_map(|ancestor| match ancestor {
Ancestor::Complete(id) => Some(CDeclaration::from_declaration(
*id,
graph.declarations().get(id).unwrap(),
)),
Ancestor::Partial(_) => None,
})
.collect::<Vec<_>>()
});
DeclarationsIter::new(declarations.into_boxed_slice())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_descendants(pointer: GraphPointer, decl_id: u64) -> *mut DeclarationsIter {
let declarations = with_graph(pointer, |graph| {
let declaration_id = DeclarationId::new(decl_id);
let Some(Declaration::Namespace(declaration)) = graph.declarations().get(&declaration_id) else {
return Vec::new();
};
declaration
.descendants()
.iter()
.map(|id| CDeclaration::from_declaration(*id, graph.declarations().get(id).unwrap()))
.collect::<Vec<_>>()
});
DeclarationsIter::new(declarations.into_boxed_slice())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_members(pointer: GraphPointer, decl_id: u64) -> *mut DeclarationsIter {
let declarations = with_graph(pointer, |graph| {
let declaration_id = DeclarationId::new(decl_id);
let Some(Declaration::Namespace(declaration)) = graph.declarations().get(&declaration_id) else {
return Vec::new();
};
declaration
.members()
.values()
.map(|id| CDeclaration::from_declaration(*id, graph.declarations().get(id).unwrap()))
.collect::<Vec<_>>()
});
DeclarationsIter::new(declarations.into_boxed_slice())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_constant_alias_target(pointer: GraphPointer, decl_id: u64) -> *const CDeclaration {
with_graph(pointer, |graph| {
let declaration_id = DeclarationId::new(decl_id);
let Some(targets) = graph.alias_targets(&declaration_id) else {
return ptr::null();
};
let Some(&target_id) = targets.first() else {
return ptr::null();
};
let target_decl = graph.declarations().get(&target_id).unwrap();
Box::into_raw(Box::new(CDeclaration::from_declaration(target_id, target_decl))).cast_const()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_constant_references_iter_new(
pointer: GraphPointer,
declaration_id: u64,
) -> *mut ConstantReferencesIter {
with_graph(pointer, |graph| {
let decl_id_typed = DeclarationId::new(declaration_id);
let Some(decl) = graph.declarations().get(&decl_id_typed) else {
return ptr::null_mut();
};
let Some(constant_references) = decl.constant_references() else {
return ptr::null_mut();
};
let entries: Vec<_> = constant_references
.iter()
.map(|ref_id| CConstantReference {
id: **ref_id,
declaration_id,
})
.collect();
ConstantReferencesIter::new(entries.into_boxed_slice())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_declaration_method_references_iter_new(
pointer: GraphPointer,
decl_id: u64,
) -> *mut MethodReferencesIter {
with_graph(pointer, |graph| {
let decl_id = DeclarationId::new(decl_id);
let Some(Declaration::Method(decl)) = graph.declarations().get(&decl_id) else {
return ptr::null_mut();
};
let entries: Vec<_> = decl
.references()
.iter()
.map(|ref_id| CMethodReference { id: **ref_id })
.collect();
MethodReferencesIter::new(entries.into_boxed_slice())
})
}