code-moniker-core 0.2.0

Core symbol-graph types and per-language extractors for code-moniker (pure Rust, no pgrx). Consumed by the CLI and the PostgreSQL extension.
Documentation
use tree_sitter::Node;

pub(crate) fn node_position(node: Node<'_>) -> (u32, u32) {
	(node.start_byte() as u32, node.end_byte() as u32)
}

pub(crate) fn node_slice<'src>(node: Node<'src>, source: &'src [u8]) -> &'src [u8] {
	&source[node.start_byte()..node.end_byte().min(source.len())]
}

pub(crate) fn find_named_child<'tree>(parent: Node<'tree>, kind: &str) -> Option<Node<'tree>> {
	let mut cursor = parent.walk();
	parent
		.named_children(&mut cursor)
		.find(|c| c.kind() == kind)
}

pub(crate) fn find_descendant<'tree>(node: Node<'tree>, kind: &str) -> Option<Node<'tree>> {
	if node.kind() == kind {
		return Some(node);
	}
	let mut cursor = node.walk();
	for c in node.named_children(&mut cursor) {
		if let Some(d) = find_descendant(c, kind) {
			return Some(d);
		}
	}
	None
}

#[cfg(test)]
mod tests {
	use super::*;

	fn parse_rust(src: &str) -> tree_sitter::Tree {
		let mut p = tree_sitter::Parser::new();
		p.set_language(&tree_sitter_rust::LANGUAGE.into()).unwrap();
		p.parse(src, None).unwrap()
	}

	#[test]
	fn node_position_returns_byte_range() {
		let tree = parse_rust("pub fn foo() {}");
		let root = tree.root_node();
		assert_eq!(node_position(root), (0, 15));
	}

	#[test]
	fn node_slice_borrows_from_source() {
		let src = b"pub fn foo() {}";
		let tree = parse_rust(std::str::from_utf8(src).unwrap());
		let root = tree.root_node();
		let slice = node_slice(root, src);
		assert_eq!(slice, b"pub fn foo() {}");
		assert_eq!(slice.as_ptr(), src.as_ptr());
	}

	#[test]
	fn find_named_child_returns_first_match() {
		let tree = parse_rust("pub fn a() {} pub fn b() {}");
		let root = tree.root_node();
		let first = find_named_child(root, "function_item").unwrap();
		assert_eq!(first.start_byte(), 0);
	}

	#[test]
	fn find_named_child_returns_none_when_absent() {
		let tree = parse_rust("pub fn a() {}");
		let root = tree.root_node();
		assert!(find_named_child(root, "struct_item").is_none());
	}

	#[test]
	fn find_descendant_is_recursive() {
		let tree = parse_rust("pub fn a() { let x = 1; }");
		let root = tree.root_node();
		assert!(find_descendant(root, "let_declaration").is_some());
	}

	#[test]
	fn find_descendant_matches_self() {
		let tree = parse_rust("pub fn a() {}");
		let root = tree.root_node();
		assert_eq!(
			find_descendant(root, "source_file").map(|n| n.start_byte()),
			Some(0)
		);
	}
}