use crate::graph_api::{GraphPointer, with_graph};
use crate::location_api::{Location, create_location_for_uri_and_offset};
use libc::c_char;
use rubydex::model::declaration::Declaration;
use rubydex::model::definitions::{Definition, MethodDefinition, Parameter};
use rubydex::model::graph::Graph;
use rubydex::model::ids::DefinitionId;
use std::ffi::CString;
use std::ptr;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ParameterKind {
RequiredPositional = 0,
OptionalPositional = 1,
RestPositional = 2,
Post = 3,
RequiredKeyword = 4,
OptionalKeyword = 5,
RestKeyword = 6,
Forward = 7,
Block = 8,
}
fn map_parameter_kind(param: &Parameter) -> ParameterKind {
match param {
Parameter::RequiredPositional(_) => ParameterKind::RequiredPositional,
Parameter::Post(_) => ParameterKind::Post,
Parameter::OptionalPositional(_) => ParameterKind::OptionalPositional,
Parameter::RestPositional(_) => ParameterKind::RestPositional,
Parameter::RequiredKeyword(_) => ParameterKind::RequiredKeyword,
Parameter::OptionalKeyword(_) => ParameterKind::OptionalKeyword,
Parameter::RestKeyword(_) => ParameterKind::RestKeyword,
Parameter::Block(_) => ParameterKind::Block,
Parameter::Forward(_) => ParameterKind::Forward,
}
}
#[repr(C)]
pub struct ParameterEntry {
pub name: *const c_char,
pub location: *mut Location,
pub kind: ParameterKind,
}
#[repr(C)]
pub struct SignatureEntry {
pub parameters: *mut ParameterEntry,
pub parameters_len: usize,
}
#[repr(C)]
pub struct SignatureArray {
pub items: *mut SignatureEntry,
pub len: usize,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_signatures(pointer: GraphPointer, definition_id: u64) -> *mut SignatureArray {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let Definition::Method(method_def) = graph.definitions().get(&def_id).expect("definition should exist") else {
panic!("expected a method definition");
};
let sig_entries = collect_method_signatures(graph, method_def);
let len = sig_entries.len();
let items_ptr = Box::into_raw(sig_entries.into_boxed_slice()).cast::<SignatureEntry>();
Box::into_raw(Box::new(SignatureArray { items: items_ptr, len }))
})
}
fn collect_method_signatures(graph: &Graph, method_def: &MethodDefinition) -> Vec<SignatureEntry> {
let uri_id = *method_def.uri_id();
let document = graph.documents().get(&uri_id).expect("document should exist");
method_def
.signatures()
.as_slice()
.iter()
.map(|sig| {
let param_entries: Vec<ParameterEntry> = sig
.iter()
.map(|param| {
let param_struct = param.inner();
let name = graph
.strings()
.get(param_struct.str())
.expect("parameter name string should exist");
let name_str = CString::new(name.as_str()).unwrap().into_raw().cast_const();
ParameterEntry {
name: name_str,
kind: map_parameter_kind(param),
location: create_location_for_uri_and_offset(graph, document, param_struct.offset()),
}
})
.collect();
let parameters_len = param_entries.len();
let parameters_ptr = Box::into_raw(param_entries.into_boxed_slice()).cast::<ParameterEntry>();
SignatureEntry {
parameters: parameters_ptr,
parameters_len,
}
})
.collect()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_method_alias_definition_signatures(
pointer: GraphPointer,
definition_id: u64,
) -> *mut SignatureArray {
with_graph(pointer, |graph| {
let def_id = DefinitionId::new(definition_id);
let resolved = rubydex::query::follow_method_alias(graph, def_id);
let mut sig_entries: Vec<SignatureEntry> = Vec::new();
if let Ok(declaration_id) = resolved {
if let Some(Declaration::Method(method_def)) = graph.declarations().get(&declaration_id) {
for definition_id in method_def.definitions() {
if let Some(Definition::Method(method_definition)) = graph.definitions().get(definition_id) {
sig_entries.extend(collect_method_signatures(graph, method_definition));
}
}
} else {
panic!("expected a method declaration");
}
}
let mut boxed = sig_entries.into_boxed_slice();
let len = boxed.len();
let items_ptr = boxed.as_mut_ptr();
std::mem::forget(boxed);
Box::into_raw(Box::new(SignatureArray { items: items_ptr, len }))
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rdx_definition_signatures_free(ptr: *mut SignatureArray) {
if ptr.is_null() {
return;
}
let arr = unsafe { Box::from_raw(ptr) };
if arr.items.is_null() || arr.len == 0 {
return;
}
let slice_ptr = ptr::slice_from_raw_parts_mut(arr.items, arr.len);
let sig_slice: Box<[SignatureEntry]> = unsafe { Box::from_raw(slice_ptr) };
for sig_entry in &*sig_slice {
if sig_entry.parameters.is_null() || sig_entry.parameters_len == 0 {
continue;
}
let param_slice_ptr = ptr::slice_from_raw_parts_mut(sig_entry.parameters, sig_entry.parameters_len);
let param_slice: Box<[ParameterEntry]> = unsafe { Box::from_raw(param_slice_ptr) };
for param_entry in &*param_slice {
if !param_entry.name.is_null() {
drop(unsafe { CString::from_raw(param_entry.name.cast_mut()) });
}
if !param_entry.location.is_null() {
unsafe { crate::location_api::rdx_location_free(param_entry.location) };
}
}
}
}