use std::ffi::CStr;
use std::ptr::NonNull;
use libperl_sys::{OP, OPf_KIDS, PL_op_name, opcode, unop};
#[cfg(perlapi_ver26)]
use libperl_sys::OPclass;
use crate::{Cop, Perl};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Op(NonNull<OP>);
impl Op {
#[inline]
pub unsafe fn from_raw_unchecked(p: *const OP) -> Self {
debug_assert!(!p.is_null(), "Op::from_raw_unchecked received a null pointer");
Op(unsafe { NonNull::new_unchecked(p as *mut OP) })
}
#[inline]
pub fn from_raw(p: *const OP) -> Option<Self> {
NonNull::new(p as *mut OP).map(Op)
}
#[inline]
pub fn as_ptr(&self) -> *mut OP {
self.0.as_ptr()
}
#[inline]
pub fn op_type_raw(&self) -> u32 {
unsafe { (*self.0.as_ptr()).op_type() as u32 }
}
#[inline]
pub fn opcode(&self) -> Option<opcode> {
opcode::try_from(self.op_type_raw()).ok()
}
pub fn name(&self) -> Option<&'static str> {
self.opcode()?;
let p = unsafe { PL_op_name[self.op_type_raw() as usize] };
unsafe { CStr::from_ptr(p) }.to_str().ok()
}
#[inline]
pub fn flags(&self) -> u8 {
unsafe { (*self.0.as_ptr()).op_flags }
}
#[inline]
pub fn next(&self) -> Option<Op> {
Op::from_raw(unsafe { (*self.0.as_ptr()).op_next })
}
#[inline]
pub fn sibling(&self) -> Option<Op> {
Op::from_raw(unsafe { libperl_sys::OpSIBLING(self.0.as_ptr()) })
}
#[inline]
pub fn first(&self) -> Option<Op> {
if (self.flags() as u32 & OPf_KIDS) == 0 {
None
} else {
Op::from_raw(unsafe { (*(self.0.as_ptr() as *const unop)).op_first })
}
}
#[inline]
pub fn kids(&self) -> OpSiblingIter {
OpSiblingIter { cur: self.first() }
}
#[inline]
pub fn next_iter(&self) -> OpNextIter {
OpNextIter { cur: Some(*self) }
}
#[cfg(perlapi_ver26)]
#[inline]
pub fn class(&self, perl: &Perl) -> OPclass {
unsafe { crate::thx_call!(perl, Perl_op_class, self.0.as_ptr()) }
}
fn is_cop(&self, perl: &Perl) -> bool {
#[cfg(perlapi_ver26)]
{
self.class(perl) == OPclass::OPclass_COP
}
#[cfg(not(perlapi_ver26))]
{
let _ = perl;
let mut t = self.op_type_raw();
if t == opcode::OP_NULL as u32 {
t = unsafe { (*self.0.as_ptr()).op_targ } as u32;
}
t == opcode::OP_NEXTSTATE as u32 || t == opcode::OP_DBSTATE as u32
}
}
#[inline]
pub fn as_cop(&self, perl: &Perl) -> Option<Cop> {
if self.is_cop(perl) {
Cop::from_raw(self.0.as_ptr() as *const libperl_sys::COP)
} else {
None
}
}
}
pub struct OpNextIter {
cur: Option<Op>,
}
impl Iterator for OpNextIter {
type Item = Op;
fn next(&mut self) -> Option<Op> {
let op = self.cur?;
self.cur = op.next();
Some(op)
}
}
pub struct OpSiblingIter {
cur: Option<Op>,
}
impl Iterator for OpSiblingIter {
type Item = Op;
fn next(&mut self) -> Option<Op> {
let op = self.cur?;
self.cur = op.sibling();
Some(op)
}
}