use crate::declaration_api::CDeclaration;
use crate::graph_api::{GraphPointer, with_graph};
use crate::location_api::{Location, create_location_for_uri_and_offset};
use crate::reference_api::CConstantReference;
use libc::c_char;
use rubydex::model::definitions::{Definition, Mixin};
use rubydex::model::ids::DefinitionId;
use std::ffi::CString;
use std::ptr;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DefinitionKind {
Class = 0,
SingletonClass = 1,
Module = 2,
Constant = 3,
ConstantAlias = 4,
ConstantVisibility = 5,
MethodVisibility = 6,
Method = 7,
AttrAccessor = 8,
AttrReader = 9,
AttrWriter = 10,
GlobalVariable = 11,
InstanceVariable = 12,
ClassVariable = 13,
MethodAlias = 14,
GlobalVariableAlias = 15,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CDefinition {
pub id: u64,
pub kind: DefinitionKind,
}
pub(crate) fn map_definition_to_kind(defn: &Definition) -> DefinitionKind {
match defn {
Definition::Class(_) => DefinitionKind::Class,
Definition::SingletonClass(_) => DefinitionKind::SingletonClass,
Definition::Module(_) => DefinitionKind::Module,
Definition::Constant(_) => DefinitionKind::Constant,
Definition::ConstantAlias(_) => DefinitionKind::ConstantAlias,
Definition::ConstantVisibility(_) => DefinitionKind::ConstantVisibility,
Definition::MethodVisibility(_) => DefinitionKind::MethodVisibility,
Definition::Method(_) => DefinitionKind::Method,
Definition::AttrAccessor(_) => DefinitionKind::AttrAccessor,
Definition::AttrReader(_) => DefinitionKind::AttrReader,
Definition::AttrWriter(_) => DefinitionKind::AttrWriter,
Definition::GlobalVariable(_) => DefinitionKind::GlobalVariable,
Definition::InstanceVariable(_) => DefinitionKind::InstanceVariable,
Definition::ClassVariable(_) => DefinitionKind::ClassVariable,
Definition::MethodAlias(_) => DefinitionKind::MethodAlias,
Definition::GlobalVariableAlias(_) => DefinitionKind::GlobalVariableAlias,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_kind(pointer: GraphPointer, definition_id: u64) -> DefinitionKind {
with_graph(pointer, |graph| {
let definition_id = DefinitionId::new(definition_id);
if let Some(defn) = graph.definitions().get(&definition_id) {
map_definition_to_kind(defn)
} else {
panic!("Definition not found: {definition_id:?}");
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_name(pointer: GraphPointer, definition_id: u64) -> *const c_char {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
if let Some(defn) = graph.definitions().get(&def_id) {
let string_id = graph.definition_string_id(defn);
if let Some(name) = graph.strings().get(&string_id) {
CString::new(name.as_str()).unwrap().into_raw().cast_const()
} else {
ptr::null()
}
} else {
ptr::null()
}
})
}
#[derive(Debug)]
pub struct DefinitionsIter {
entries: Box<[CDefinition]>,
index: usize,
}
iterator!(DefinitionsIter, entries: CDefinition);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definitions_iter_len(iter: *const DefinitionsIter) -> usize {
unsafe { DefinitionsIter::len(iter) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definitions_iter_next(iter: *mut DefinitionsIter, out: *mut CDefinition) -> bool {
unsafe { DefinitionsIter::next(iter, out) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definitions_iter_free(iter: *mut DefinitionsIter) {
unsafe { DefinitionsIter::free(iter) }
}
#[repr(C)]
pub struct CommentEntry {
pub string: *const c_char,
pub location: *mut Location,
}
#[repr(C)]
pub struct CommentArray {
pub items: *mut CommentEntry,
pub len: usize,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definition_id: u64) -> *mut CommentArray {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let Some(defn) = graph.definitions().get(&def_id) else {
panic!("Definition not found: {definition_id:?}");
};
let uri_id = *defn.uri_id();
let document = graph.documents().get(&uri_id).expect("document should exist");
let mut entries = defn
.comments()
.iter()
.map(|c| CommentEntry {
string: CString::new(c.string().as_str()).unwrap().into_raw().cast_const(),
location: create_location_for_uri_and_offset(graph, document, c.offset()),
})
.collect::<Vec<CommentEntry>>()
.into_boxed_slice();
let len = entries.len();
let items_ptr = entries.as_mut_ptr();
std::mem::forget(entries);
Box::into_raw(Box::new(CommentArray { items: items_ptr, len }))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_comments_free(ptr: *mut CommentArray) {
if ptr.is_null() {
return;
}
let arr = unsafe { Box::from_raw(ptr) };
if !arr.items.is_null() && arr.len > 0 {
let slice_ptr = ptr::slice_from_raw_parts_mut(arr.items, arr.len);
let mut boxed_slice: Box<[CommentEntry]> = unsafe { Box::from_raw(slice_ptr) };
for item in &mut boxed_slice {
if !item.string.is_null() {
let _ = unsafe { CString::from_raw(item.string.cast_mut()) };
}
if !item.location.is_null() {
unsafe { crate::location_api::rdx_location_free(item.location) };
item.location = ptr::null_mut();
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_location(pointer: GraphPointer, definition_id: u64) -> *mut Location {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let Some(defn) = graph.definitions().get(&def_id) else {
panic!("Definition not found: {definition_id:?}");
};
let document = graph.documents().get(defn.uri_id()).expect("document should exist");
create_location_for_uri_and_offset(graph, document, defn.offset())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_declaration(pointer: GraphPointer, definition_id: u64) -> *const CDeclaration {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let Some(decl_id) = graph.definition_id_to_declaration_id(def_id) else {
return ptr::null();
};
let Some(decl) = graph.declarations().get(decl_id) else {
return ptr::null();
};
Box::into_raw(Box::new(CDeclaration::from_declaration(*decl_id, decl))).cast_const()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_lexical_nesting_id(pointer: GraphPointer, definition_id: u64) -> *const u64 {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let Some(defn) = graph.definitions().get(&def_id) else {
panic!("Definition not found: {definition_id:?}");
};
match defn.lexical_nesting_id() {
Some(lexical_nesting_id) => Box::into_raw(Box::new(**lexical_nesting_id)).cast_const(),
None => ptr::null(),
}
})
}
pub(crate) fn rdx_definitions_iter_new_from_ids<'a, I>(
graph: &rubydex::model::graph::Graph,
ids: I,
) -> *mut DefinitionsIter
where
I: IntoIterator<Item = &'a DefinitionId>,
{
let entries = ids
.into_iter()
.map(|def_id| {
let id = **def_id;
let kind = graph
.definitions()
.get(&DefinitionId::new(id))
.map_or_else(|| panic!("Definition not found: {id:?}"), map_definition_to_kind);
CDefinition { id, kind }
})
.collect::<Vec<_>>()
.into_boxed_slice();
DefinitionsIter::new(entries)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_is_deprecated(pointer: GraphPointer, definition_id: u64) -> bool {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let defn = graph.definitions().get(&def_id).expect("definition not found");
defn.is_deprecated()
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_name_location(pointer: GraphPointer, definition_id: u64) -> *mut Location {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let Some(defn) = graph.definitions().get(&def_id) else {
return ptr::null_mut();
};
let Some(name_offset) = defn.name_offset() else {
return ptr::null_mut();
};
let document = graph.documents().get(defn.uri_id()).expect("document should exist");
create_location_for_uri_and_offset(graph, document, name_offset)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_class_definition_superclass(
pointer: GraphPointer,
definition_id: u64,
) -> *const CConstantReference {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let defn = graph.definitions().get(&def_id).expect("Definition not found");
let Definition::Class(class_def) = defn else {
panic!("Definition is not a class: {definition_id}");
};
let Some(ref_id) = class_def.superclass_ref() else {
return ptr::null();
};
Box::into_raw(Box::new(CConstantReference::from_id(graph, *ref_id))).cast_const()
})
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum MixinKind {
Include = 0,
Prepend = 1,
Extend = 2,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CMixin {
pub kind: MixinKind,
pub constant_reference: CConstantReference,
}
#[derive(Debug)]
pub struct MixinsIter {
entries: Box<[CMixin]>,
index: usize,
}
iterator!(MixinsIter, entries: CMixin);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_mixins_iter_len(iter: *const MixinsIter) -> usize {
unsafe { MixinsIter::len(iter) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_mixins_iter_next(iter: *mut MixinsIter, out: *mut CMixin) -> bool {
unsafe { MixinsIter::next(iter, out) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_mixins_iter_free(iter: *mut MixinsIter) {
unsafe { MixinsIter::free(iter) }
}
fn map_mixin_kind(mixin: &Mixin) -> MixinKind {
match mixin {
Mixin::Include(_) => MixinKind::Include,
Mixin::Prepend(_) => MixinKind::Prepend,
Mixin::Extend(_) => MixinKind::Extend,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_mixins(pointer: GraphPointer, definition_id: u64) -> *mut MixinsIter {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let defn = graph.definitions().get(&def_id).expect("Definition not found");
let mixins = match defn {
Definition::Class(class_def) => class_def.mixins(),
Definition::Module(mod_def) => mod_def.mixins(),
Definition::SingletonClass(singleton_def) => singleton_def.mixins(),
_ => return ptr::null_mut(),
};
let entries: Vec<CMixin> = mixins
.iter()
.map(|mixin| CMixin {
kind: map_mixin_kind(mixin),
constant_reference: CConstantReference::from_id(graph, *mixin.constant_reference_id()),
})
.collect();
MixinsIter::new(entries.into_boxed_slice())
})
}