use controlled_option::ControlledOption;
use either::Either;
use libc::c_char;
use stack_graphs::c::sg_deque_direction;
use stack_graphs::c::sg_file_handle;
use stack_graphs::c::sg_node;
use stack_graphs::c::sg_node_handle;
use stack_graphs::c::sg_node_id;
use stack_graphs::c::sg_node_kind;
use stack_graphs::c::sg_path_arena_add_path_edge_lists;
use stack_graphs::c::sg_path_arena_add_scope_stacks;
use stack_graphs::c::sg_path_arena_add_symbol_stacks;
use stack_graphs::c::sg_path_arena_free;
use stack_graphs::c::sg_path_arena_new;
use stack_graphs::c::sg_path_arena_path_edge_list_cells;
use stack_graphs::c::sg_path_arena_scope_stack_cells;
use stack_graphs::c::sg_path_arena_symbol_stack_cells;
use stack_graphs::c::sg_path_edge;
use stack_graphs::c::sg_path_edge_list;
use stack_graphs::c::sg_path_edge_list_cells;
use stack_graphs::c::sg_scope_stack;
use stack_graphs::c::sg_scope_stack_cells;
use stack_graphs::c::sg_scoped_symbol;
use stack_graphs::c::sg_stack_graph;
use stack_graphs::c::sg_stack_graph_add_files;
use stack_graphs::c::sg_stack_graph_add_symbols;
use stack_graphs::c::sg_stack_graph_free;
use stack_graphs::c::sg_stack_graph_get_or_create_nodes;
use stack_graphs::c::sg_stack_graph_new;
use stack_graphs::c::sg_symbol_handle;
use stack_graphs::c::sg_symbol_stack;
use stack_graphs::c::sg_symbol_stack_cells;
use stack_graphs::c::SG_LIST_EMPTY_HANDLE;
use stack_graphs::paths::PathEdgeList;
use stack_graphs::paths::ScopeStack;
use stack_graphs::paths::SymbolStack;
fn add_file(graph: *mut sg_stack_graph, filename: &str) -> sg_file_handle {
let lengths = [filename.len()];
let mut handles: [sg_file_handle; 1] = [0; 1];
sg_stack_graph_add_files(
graph,
1,
filename.as_bytes().as_ptr() as *const c_char,
lengths.as_ptr(),
handles.as_mut_ptr(),
);
assert!(handles[0] != 0);
handles[0]
}
fn add_symbol(graph: *mut sg_stack_graph, value: &str) -> sg_symbol_handle {
let lengths = [value.len()];
let mut handles: [sg_symbol_handle; 1] = [0; 1];
sg_stack_graph_add_symbols(
graph,
1,
value.as_bytes().as_ptr() as *const c_char,
lengths.as_ptr(),
handles.as_mut_ptr(),
);
assert!(handles[0] != 0);
handles[0]
}
fn add_exported_scope(
graph: *mut sg_stack_graph,
file: sg_file_handle,
local_id: u32,
) -> sg_node_handle {
let node = sg_node {
kind: sg_node_kind::SG_NODE_KIND_EXPORTED_SCOPE,
id: sg_node_id { file, local_id },
symbol: 0,
is_clickable: false,
scope: sg_node_id::default(),
};
let nodes = [node];
let mut handles: [sg_node_handle; 1] = [0; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
handles[0]
}
fn empty_scope_stack() -> sg_scope_stack {
sg_scope_stack {
cells: 0,
length: 0,
}
}
fn scoped_symbol(symbol: sg_symbol_handle, scopes: sg_scope_stack) -> sg_scoped_symbol {
sg_scoped_symbol { symbol, scopes }
}
fn symbol_stack_contains(
cells: &sg_symbol_stack_cells,
stack: &sg_symbol_stack,
expected: &[sg_scoped_symbol],
) -> bool {
let cells = unsafe { std::slice::from_raw_parts(cells.cells, cells.count) };
let mut current = stack.cells;
for node in expected {
if current == SG_LIST_EMPTY_HANDLE {
return false;
}
let cell = &cells[current as usize];
if cell.head != *node {
return false;
}
current = cell.tail;
}
current == SG_LIST_EMPTY_HANDLE
}
#[test]
fn can_create_symbol_stacks() {
let graph = sg_stack_graph_new();
let paths = sg_path_arena_new();
let file = add_file(graph, "test.py");
let a = add_symbol(graph, "a");
let b = add_symbol(graph, "b");
let c = add_symbol(graph, "c");
let node1 = add_exported_scope(graph, file, 1);
let node2 = add_exported_scope(graph, file, 2);
let scopes = [node1, node2];
let lengths = [scopes.len()];
let mut scope_stacks = [sg_scope_stack::default(); 1];
sg_path_arena_add_scope_stacks(
paths,
lengths.len(),
scopes.as_ptr(),
lengths.as_ptr(),
scope_stacks.as_mut_ptr(),
);
let scope_stack = scope_stacks[0];
let symbols0 = [];
let symbols1 = [scoped_symbol(a, empty_scope_stack())];
let symbols2 = [
scoped_symbol(b, scope_stack),
scoped_symbol(c, empty_scope_stack()),
scoped_symbol(b, empty_scope_stack()),
];
let lengths = [symbols0.len(), symbols1.len(), symbols2.len()];
let mut symbolses = Vec::new();
symbolses.extend_from_slice(&symbols0);
symbolses.extend_from_slice(&symbols1);
symbolses.extend_from_slice(&symbols2);
let mut stacks = [sg_symbol_stack::default(); 3];
sg_path_arena_add_symbol_stacks(
paths,
lengths.len(),
symbolses.as_slice().as_ptr(),
lengths.as_ptr(),
stacks.as_mut_ptr(),
);
let cells = sg_path_arena_symbol_stack_cells(paths);
assert!(symbol_stack_contains(&cells, &stacks[0], &symbols0));
assert!(symbol_stack_contains(&cells, &stacks[1], &symbols1));
assert!(symbol_stack_contains(&cells, &stacks[2], &symbols2));
sg_path_arena_free(paths);
sg_stack_graph_free(graph);
}
#[test]
#[allow(unused_assignments)]
fn verify_null_symbol_stack_representation() {
let bytes = [0x55u8; std::mem::size_of::<SymbolStack>()];
let mut rust: ControlledOption<SymbolStack> = unsafe { std::mem::transmute(bytes) };
rust = ControlledOption::none();
let c: sg_symbol_stack = unsafe { std::mem::transmute(rust) };
assert_eq!(c.cells, 0);
}
fn scope_stack_contains(
cells: &sg_scope_stack_cells,
stack: &sg_scope_stack,
expected: &[sg_node_handle],
) -> bool {
let cells = unsafe { std::slice::from_raw_parts(cells.cells, cells.count) };
let mut current = stack.cells;
for node in expected {
if current == SG_LIST_EMPTY_HANDLE {
return false;
}
let cell = &cells[current as usize];
if cell.head != *node {
return false;
}
current = cell.tail;
}
current == SG_LIST_EMPTY_HANDLE
}
#[test]
fn can_create_scope_stacks() {
let graph = sg_stack_graph_new();
let paths = sg_path_arena_new();
let file = add_file(graph, "test.py");
let node1 = add_exported_scope(graph, file, 1);
let node2 = add_exported_scope(graph, file, 2);
let node3 = add_exported_scope(graph, file, 3);
let node4 = add_exported_scope(graph, file, 4);
let scopes0 = [];
let scopes1 = [node1];
let scopes2 = [node2, node3, node4];
let lengths = [scopes0.len(), scopes1.len(), scopes2.len()];
let mut scopeses = Vec::new();
scopeses.extend_from_slice(&scopes0);
scopeses.extend_from_slice(&scopes1);
scopeses.extend_from_slice(&scopes2);
let mut stacks = [sg_scope_stack::default(); 3];
sg_path_arena_add_scope_stacks(
paths,
lengths.len(),
scopeses.as_slice().as_ptr(),
lengths.as_ptr(),
stacks.as_mut_ptr(),
);
let cells = sg_path_arena_scope_stack_cells(paths);
assert!(scope_stack_contains(&cells, &stacks[0], &scopes0));
assert!(scope_stack_contains(&cells, &stacks[1], &scopes1));
assert!(scope_stack_contains(&cells, &stacks[2], &scopes2));
sg_path_arena_free(paths);
sg_stack_graph_free(graph);
}
#[test]
#[allow(unused_assignments)]
fn verify_null_scope_stack_representation() {
let bytes = [0x55u8; std::mem::size_of::<ScopeStack>()];
let mut rust: ControlledOption<ScopeStack> = unsafe { std::mem::transmute(bytes) };
rust = ControlledOption::none();
let c: sg_scope_stack = unsafe { std::mem::transmute(rust) };
assert_eq!(c.cells, 0);
}
fn path_edge(file: sg_file_handle, local_id: u32, precedence: i32) -> sg_path_edge {
let source_node_id = sg_node_id { file, local_id };
sg_path_edge {
source_node_id,
precedence,
}
}
fn path_edge_list_contains(
cells: &sg_path_edge_list_cells,
list: &sg_path_edge_list,
expected: &[sg_path_edge],
) -> bool {
let cells = unsafe { std::slice::from_raw_parts(cells.cells, cells.count) };
let mut current = list.cells;
let expected = if list.direction == sg_deque_direction::SG_DEQUE_FORWARDS {
Either::Left(expected.iter())
} else {
Either::Right(expected.iter().rev())
};
for node in expected {
if current == SG_LIST_EMPTY_HANDLE {
return false;
}
let cell = &cells[current as usize];
if cell.head != *node {
return false;
}
current = cell.tail;
}
current == SG_LIST_EMPTY_HANDLE
}
fn path_edge_list_available_in_both_directions(
cells: &sg_path_edge_list_cells,
list: &sg_path_edge_list,
) -> bool {
let cells = unsafe { std::slice::from_raw_parts(cells.cells, cells.count) };
let head = list.cells;
if head == SG_LIST_EMPTY_HANDLE {
return true;
}
let cell = &cells[head as usize];
cell.reversed != 0
}
#[test]
fn can_create_path_edge_lists() {
let graph = sg_stack_graph_new();
let paths = sg_path_arena_new();
let file = add_file(graph, "test.py");
let edges0 = [];
let edges1 = [path_edge(file, 25, 25)];
let edges2 = [
path_edge(file, 1, 11),
path_edge(file, 2, 12),
path_edge(file, 3, 13),
];
let lengths = [edges0.len(), edges1.len(), edges2.len()];
let mut edgeses = Vec::new();
edgeses.extend_from_slice(&edges0);
edgeses.extend_from_slice(&edges1);
edgeses.extend_from_slice(&edges2);
let mut lists = [sg_path_edge_list::default(); 3];
sg_path_arena_add_path_edge_lists(
paths,
lengths.len(),
edgeses.as_slice().as_ptr(),
lengths.as_ptr(),
lists.as_mut_ptr(),
);
let cells = sg_path_arena_path_edge_list_cells(paths);
assert!(path_edge_list_contains(&cells, &lists[0], &edges0));
assert!(path_edge_list_contains(&cells, &lists[1], &edges1));
assert!(path_edge_list_contains(&cells, &lists[2], &edges2));
assert!(path_edge_list_available_in_both_directions(
&cells, &lists[0]
));
assert!(path_edge_list_available_in_both_directions(
&cells, &lists[1]
));
assert!(path_edge_list_available_in_both_directions(
&cells, &lists[2]
));
sg_path_arena_free(paths);
sg_stack_graph_free(graph);
}
#[test]
#[allow(unused_assignments)]
fn verify_null_path_edge_list_representation() {
let bytes = [0x55u8; std::mem::size_of::<PathEdgeList>()];
let mut rust: ControlledOption<PathEdgeList> = unsafe { std::mem::transmute(bytes) };
rust = ControlledOption::none();
let c: sg_path_edge_list = unsafe { std::mem::transmute(rust) };
assert_eq!(c.cells, 0);
}