use tree_sitter::Node;
use crate::error::Result;
use crate::types::{Chunk, ChunkKind, FileHash, Language, RelativePath};
use super::{chunk_with_grammar_named, default_name};
pub(super) fn chunk(path: &RelativePath, file_hash: FileHash, content: &str) -> Result<Vec<Chunk>> {
chunk_with_grammar_named(
tree_sitter_c::LANGUAGE.into(),
"C",
path,
Language::C,
file_hash,
content,
c_chunk_kind,
c_name,
)
}
fn c_chunk_kind(node: Node<'_>) -> Option<ChunkKind> {
match node.kind() {
"function_definition" => Some(ChunkKind::Function),
"struct_specifier" | "union_specifier" => has_body(node).then_some(ChunkKind::Struct),
"enum_specifier" => has_body(node).then_some(ChunkKind::Enum),
"preproc_def" | "preproc_function_def" => Some(ChunkKind::Macro),
_ => None,
}
}
fn has_body(node: Node<'_>) -> bool {
node.child_by_field_name("body").is_some()
}
fn c_name(node: Node<'_>, content: &str) -> Option<String> {
if node.kind() == "function_definition" {
return node
.child_by_field_name("declarator")
.and_then(|declarator| declarator_name(declarator, content));
}
if let Some(name) = default_name(node, content) {
return Some(name);
}
node.parent()
.filter(|parent| parent.kind() == "type_definition")
.and_then(|parent| parent.child_by_field_name("declarator"))
.and_then(|declarator| declarator_name(declarator, content))
}
fn declarator_name(node: Node<'_>, content: &str) -> Option<String> {
match node.kind() {
"identifier" | "type_identifier" | "field_identifier" => {
node.utf8_text(content.as_bytes()).ok().map(str::to_owned)
}
_ => node
.child_by_field_name("declarator")
.and_then(|child| declarator_name(child, content)),
}
}