use std::ffi::{CStr, CString};
use crate::error::MjEditError;
use crate::mujoco_c::*;
use super::default::MjsDefault;
use super::utility::*;
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait SpecItem: Sized + sealed::Sealed {
fn element_pointer(&self) -> *const mjsElement;
fn element_mut_pointer(&mut self) -> *mut mjsElement {
self.element_pointer() as *mut _
}
fn name(&self) -> &str {
unsafe { read_mjs_string(mjs_getName(self.element_pointer() as *mut _)) }
}
fn set_name(&mut self, name: &str) -> Result<(), MjEditError> {
let cstr = CString::new(name).unwrap(); let result = unsafe { mjs_setName(self.element_mut_pointer(), cstr.as_ptr()) };
if result != 0 {
return Err(MjEditError::AlreadyExists);
}
Ok(())
}
fn with_name(&mut self, name: &str) -> &mut Self {
self.set_name(name).expect("mjs_setName failed: duplicate name or null byte");
self
}
fn default(&self) -> &MjsDefault {
unsafe { &*mjs_getDefault(self.element_pointer()) }
}
fn id(&self) -> Option<usize> {
let id = unsafe { mjs_getId(self.element_pointer()) };
usize::try_from(id).ok()
}
fn set_default(&mut self, class_name: &str) -> Result<(), MjEditError> {
let cname = CString::new(class_name).unwrap(); let element = self.element_pointer();
let spec = unsafe { mjs_getSpec(element) };
let default = unsafe { mjs_findDefault(spec, cname.as_ptr()) };
if default.is_null() {
return Err(MjEditError::NotFound);
}
unsafe { mjs_setDefault(self.element_mut_pointer(), default); }
Ok(())
}
fn with_default(&mut self, class_name: &str) -> Result<&mut Self, MjEditError> {
self.set_default(class_name)?;
Ok(self)
}
#[deprecated(
since = "5.0.0",
note = "unsound legacy API; use MjSpec::delete_element(element_mut_pointer())"
)]
unsafe fn delete(&mut self) -> Result<(), MjEditError> {
unsafe { self.__delete_default__() }
}
unsafe fn __delete_default__(&mut self) -> Result<(), MjEditError> {
let element = self.element_mut_pointer();
let spec = unsafe { mjs_getSpec(element) };
let result = unsafe { mjs_delete(spec, element) };
match result {
0 => Ok(()),
_ => {
let error_msg: String = unsafe {
let ptr = mjs_getError(spec);
if ptr.is_null() {
"Unknown error".to_owned()
} else {
CStr::from_ptr(ptr).to_string_lossy().into_owned()
}
};
Err(MjEditError::DeleteFailed(error_msg))
}
}
}
}
pub trait SpecObject: SpecItem {
const OBJ_TYPE: mjtObj;
unsafe fn from_element_as_ptr_mut(ptr: *mut mjsElement) -> *mut Self;
}