use super::*;
use extendr_ffi::{R_do_slot, R_do_slot_assign, R_has_slot};
#[derive(PartialEq, Clone)]
pub struct S4 {
pub(crate) robj: Robj,
}
impl S4 {
pub fn set_class(name: &str, representation: Pairlist, contains: Robj) -> Result<S4> {
use crate as extendr_api;
let res = R!(r#"setClass({{name}}, {{representation}}, {{contains}})"#)?;
res.try_into()
}
pub fn new(name: &str) -> Result<S4> {
use crate as extendr_api;
let res = R!(r#"new({{name}})"#)?;
res.try_into()
}
pub fn get_slot<'a, N>(&self, name: N) -> Option<Robj>
where
Self: 'a,
Robj: From<N> + 'a,
{
let name = Robj::from(name);
unsafe {
if R_has_slot(self.get(), name.get()) != 0 {
Some(Robj::from_sexp(R_do_slot(self.get(), name.get())))
} else {
None
}
}
}
pub fn set_slot<N, V>(&mut self, name: N, value: V) -> Result<S4>
where
N: Into<Robj>,
V: Into<Robj>,
{
let name = name.into();
let value = value.into();
single_threaded(|| unsafe {
catch_r_error(|| R_do_slot_assign(self.get(), name.get(), value.get()))
.map(|_| self.clone())
})
}
pub fn has_slot<'a, N>(&self, name: N) -> bool
where
Self: 'a,
Robj: From<N> + 'a,
{
let name = Robj::from(name);
unsafe { R_has_slot(self.get(), name.get()) != 0 }
}
}
impl std::fmt::Debug for S4 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("S4").finish()
}
}
impl From<Option<S4>> for Robj {
fn from(value: Option<S4>) -> Self {
match value {
None => nil_value(),
Some(value) => value.into(),
}
}
}