use std::ptr::{self, NonNull};
use crate::{Error, ErrorKind, Node, Parser, Result, Tree, ffi};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum ForeignPolicy {
Refuse = 0,
Admit = 1,
Deny = 2,
}
pub struct Query<'parser> {
raw: NonNull<ffi::Handle>,
parser: &'parser Parser<'parser>,
}
#[derive(Clone, Copy, Debug)]
pub struct Capture<'query, 'tree> {
pub id: u32,
pub name: &'query [u8],
pub node: Node<'tree>,
}
#[derive(Debug)]
pub struct Match<'query, 'tree> {
pub pattern: u32,
pub captures: Vec<Capture<'query, 'tree>>,
}
impl<'parser> Query<'parser> {
pub(crate) fn compile(parser: &'parser Parser<'parser>, source: &str) -> Result<Self> {
let mut raw = ptr::null_mut();
Error::status(unsafe {
ffi::jnt_query_compile(
parser.raw.as_ptr(),
source.as_ptr().cast(),
source.len(),
&mut raw,
)
})?;
Ok(Self {
raw: Error::handle(raw)?,
parser,
})
}
pub fn pattern_count(&self) -> u32 {
unsafe { ffi::jnt_query_pattern_count(self.raw.as_ptr()) }
}
pub fn capture_count(&self) -> u32 {
unsafe { ffi::jnt_query_capture_count(self.raw.as_ptr()) }
}
pub fn capture_name(&self, id: u32) -> Option<&[u8]> {
let mut len = 0;
unsafe {
let pointer = ffi::jnt_query_capture_name(self.raw.as_ptr(), id, &mut len);
(!pointer.is_null()).then(|| ffi::bytes(pointer, len))
}
}
pub fn matches<'query, 'tree>(
&'query self,
tree: &'tree Tree<'tree>,
foreign: ForeignPolicy,
) -> Result<Vec<Match<'query, 'tree>>> {
if tree.parser.raw != self.parser.raw {
return Err(Error::new(
ErrorKind::Invalid,
"query and tree belong to different parsers",
));
}
let mut raw = ptr::null_mut();
Error::status(unsafe {
ffi::jnt_query_exec(
self.raw.as_ptr(),
tree.raw.as_ptr(),
tree.source().as_ptr().cast(),
tree.source().len(),
foreign as i32,
&mut raw,
)
})?;
let result = NativeResult(Error::handle(raw)?);
let count = unsafe { ffi::jnt_query_result_count(result.0.as_ptr()) };
(0..count)
.map(|i| {
let (pattern, captures) = unsafe {
(
ffi::jnt_query_result_pattern(result.0.as_ptr(), i),
ffi::jnt_query_result_capture_count(result.0.as_ptr(), i),
)
};
let captures = (0..captures)
.map(|capture| {
let (id, node) = unsafe {
(
ffi::jnt_query_result_capture_id(result.0.as_ptr(), i, capture),
ffi::jnt_query_result_capture_node(result.0.as_ptr(), i, capture),
)
};
let name = self.capture_name(id).ok_or_else(|| {
Error::new(ErrorKind::Format, "invalid native capture ID")
})?;
let node = tree.node(node).ok_or_else(|| {
Error::new(ErrorKind::Format, "invalid native captured node")
})?;
Ok(Capture { id, name, node })
})
.collect::<Result<Vec<_>>>()?;
Ok(Match { pattern, captures })
})
.collect()
}
}
impl Drop for Query<'_> {
fn drop(&mut self) {
unsafe { ffi::jnt_query_free(self.raw.as_ptr()) };
}
}
struct NativeResult(NonNull<ffi::Handle>);
impl Drop for NativeResult {
fn drop(&mut self) {
unsafe { ffi::jnt_query_result_free(self.0.as_ptr()) };
}
}