use crate::maths::{Bool32T, Matrix};
use crate::sk::MainThreadToken;
use crate::{
StereoKitError,
material::{Cull, Material, MaterialT},
maths::{Bounds, Ray, Vec3},
mesh::{Mesh, MeshT},
shader::{Shader, ShaderT},
system::{IAsset, Log, RenderLayer},
util::Color128,
};
use std::{
ffi::{CStr, CString, c_char, c_void},
path::Path,
ptr::{NonNull, null_mut},
};
#[derive(Debug, PartialEq)]
pub struct Model(pub NonNull<_ModelT>);
impl Drop for Model {
fn drop(&mut self) {
unsafe { model_release(self.0.as_ptr()) }
}
}
impl AsRef<Model> for Model {
fn as_ref(&self) -> &Model {
self
}
}
impl From<Model> for ModelT {
fn from(val: Model) -> Self {
val.0.as_ptr()
}
}
#[repr(C)]
#[derive(Debug)]
pub struct _ModelT {
_unused: [u8; 0],
}
pub type ModelT = *mut _ModelT;
unsafe extern "C" {
pub fn model_find(id: *const c_char) -> ModelT;
pub fn model_copy(model: ModelT) -> ModelT;
pub fn model_create() -> ModelT;
pub fn model_create_mesh(mesh: MeshT, material: MaterialT) -> ModelT;
pub fn model_create_mem(
filename_utf8: *const c_char,
data: *const c_void,
data_size: usize,
shader: ShaderT,
) -> ModelT;
pub fn model_create_file(filename_utf8: *const c_char, shader: ShaderT) -> ModelT;
pub fn model_set_id(model: ModelT, id: *const c_char);
pub fn model_get_id(model: ModelT) -> *const c_char;
pub fn model_addref(model: ModelT);
pub fn model_release(model: ModelT);
pub fn model_draw(model: ModelT, transform: Matrix, color_linear: Color128, layer: RenderLayer);
pub fn model_draw_mat(
model: ModelT,
material_override: MaterialT,
transform: Matrix,
color_linear: Color128,
layer: RenderLayer,
);
pub fn model_recalculate_bounds(model: ModelT);
pub fn model_recalculate_bounds_exact(model: ModelT);
pub fn model_set_bounds(model: ModelT, bounds: *const Bounds);
pub fn model_get_bounds(model: ModelT) -> Bounds;
pub fn model_ray_intersect(model: ModelT, model_space_ray: Ray, cull_mode: Cull, out_pt: *mut Ray) -> Bool32T;
pub fn model_ray_intersect_bvh(model: ModelT, model_space_ray: Ray, cull_mode: Cull, out_pt: *mut Ray) -> Bool32T;
pub fn model_ray_intersect_bvh_detailed(
model: ModelT,
model_space_ray: Ray,
cull_mode: Cull,
out_pt: *mut Ray,
out_mesh: *mut MeshT,
out_matrix: *mut Matrix,
out_start_inds: *mut u32,
) -> Bool32T;
pub fn model_step_anim(model: ModelT);
pub fn model_play_anim(model: ModelT, animation_name: *const c_char, mode: AnimMode) -> Bool32T;
pub fn model_play_anim_idx(model: ModelT, index: i32, mode: AnimMode);
pub fn model_set_anim_time(model: ModelT, time: f32);
pub fn model_set_anim_completion(model: ModelT, percent: f32);
pub fn model_anim_find(model: ModelT, animation_name: *const c_char) -> i32;
pub fn model_anim_count(model: ModelT) -> i32;
pub fn model_anim_active(model: ModelT) -> i32;
pub fn model_anim_active_mode(model: ModelT) -> AnimMode;
pub fn model_anim_active_time(model: ModelT) -> f32;
pub fn model_anim_active_completion(model: ModelT) -> f32;
pub fn model_anim_get_name(model: ModelT, index: i32) -> *const c_char;
pub fn model_anim_get_duration(model: ModelT, index: i32) -> f32;
}
impl IAsset for Model {
fn get_id(&self) -> &str {
self.get_id()
}
}
impl Default for Model {
fn default() -> Self {
Self::new()
}
}
impl Model {
pub fn new() -> Model {
Model(NonNull::new(unsafe { model_create() }).unwrap())
}
pub fn from_mesh<Me: AsRef<Mesh>, Ma: AsRef<Material>>(mesh: Me, material: Ma) -> Model {
Model(
NonNull::new(unsafe { model_create_mesh(mesh.as_ref().0.as_ptr(), material.as_ref().0.as_ptr()) }).unwrap(),
)
}
pub fn from_memory<S: AsRef<str>>(
file_name: S,
data: &[u8],
shader: Option<Shader>,
) -> Result<Model, StereoKitError> {
let c_file_name = CString::new(file_name.as_ref())?;
let shader = shader.map(|shader| shader.0.as_ptr()).unwrap_or(null_mut());
match NonNull::new(unsafe {
model_create_mem(c_file_name.as_ptr(), data.as_ptr() as *const c_void, data.len(), shader)
}) {
Some(model) => Ok(Model(model)),
None => Err(StereoKitError::ModelFromMem(file_name.as_ref().to_owned(), "file not found!".to_owned())),
}
}
pub fn from_file(file: impl AsRef<Path>, shader: Option<Shader>) -> Result<Model, StereoKitError> {
let path = file.as_ref();
let path_buf = path.to_path_buf();
let c_str = CString::new(path.to_str().unwrap())?;
let shader = shader.map(|shader| shader.0.as_ptr()).unwrap_or(null_mut());
match NonNull::new(unsafe { model_create_file(c_str.as_ptr(), shader) }) {
Some(model) => Ok(Model(model)),
None => Err(StereoKitError::ModelFromFile(path_buf.to_owned(), "file not found!".to_owned())),
}
}
pub fn copy(&self) -> Model {
Model(NonNull::new(unsafe { model_copy(self.0.as_ptr()) }).unwrap())
}
pub fn find<S: AsRef<str>>(id: S) -> Result<Model, StereoKitError> {
let c_str = CString::new(id.as_ref())?;
match NonNull::new(unsafe { model_find(c_str.as_ptr()) }) {
Some(model) => Ok(Model(model)),
None => Err(StereoKitError::ModelFind(id.as_ref().to_owned())),
}
}
pub fn clone_ref(&self) -> Model {
Model(NonNull::new(unsafe { model_find(model_get_id(self.0.as_ptr())) }).expect("<asset>::clone_ref failed!"))
}
pub fn id<S: AsRef<str>>(&mut self, id: S) -> &mut Self {
let c_str = CString::new(id.as_ref()).unwrap();
unsafe { model_set_id(self.0.as_ptr(), c_str.as_ptr()) };
self
}
pub fn bounds(&mut self, bounds: impl AsRef<Bounds>) -> &mut Self {
unsafe { model_set_bounds(self.0.as_ptr(), bounds.as_ref()) };
self
}
pub fn draw(
&self,
_token: &MainThreadToken,
transform: impl Into<Matrix>,
color_linear: Option<Color128>,
layer: Option<RenderLayer>,
) {
let color_linear = color_linear.unwrap_or(Color128::WHITE);
let layer = layer.unwrap_or(RenderLayer::Layer0);
unsafe { model_draw(self.0.as_ptr(), transform.into(), color_linear, layer) };
}
pub fn draw_with_material<M: AsRef<Material>>(
&self,
_token: &MainThreadToken,
material_override: M,
transform: impl Into<Matrix>,
color_linear: Option<Color128>,
layer: Option<RenderLayer>,
) {
let color_linear = match color_linear {
Some(c) => c,
None => Color128::WHITE,
};
let layer = layer.unwrap_or(RenderLayer::Layer0);
unsafe {
model_draw_mat(
self.0.as_ptr(),
material_override.as_ref().0.as_ptr(),
transform.into(),
color_linear,
layer,
)
};
}
pub fn recalculate_bounds(&self) {
unsafe { model_recalculate_bounds(self.0.as_ptr()) };
}
pub fn recalculate_bounds_exact(&self) {
unsafe { model_recalculate_bounds_exact(self.0.as_ptr()) };
}
pub fn get_id(&self) -> &str {
unsafe { CStr::from_ptr(model_get_id(self.0.as_ptr())) }.to_str().unwrap()
}
pub fn get_bounds(&self) -> Bounds {
unsafe { model_get_bounds(self.0.as_ptr()) }
}
pub fn get_nodes(&'_ self) -> Nodes<'_> {
Nodes::from(self)
}
pub fn get_anims(&'_ self) -> Anims<'_> {
Anims::from(self)
}
#[inline]
pub fn intersect(&self, ray: Ray, cull: Option<Cull>) -> Option<Vec3> {
ray.intersect_model(self, cull)
}
#[inline]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn intersect_to_ptr(&self, ray: Ray, cull: Option<Cull>, out_ray: *mut Ray) -> bool {
ray.intersect_model_to_ptr(self, cull, out_ray)
}
}
pub struct Anims<'a> {
model: &'a Model,
curr: i32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u32)]
pub enum AnimMode {
Loop = 0,
Once = 1,
Manual = 2,
}
impl Iterator for Anims<'_> {
type Item = Anim;
fn next(&mut self) -> Option<Self::Item> {
self.curr += 1;
if self.curr < self.get_count() {
Some(Anim {
name: match self.get_name_at_index(self.curr) {
Some(name) => name.to_string(),
None => {
Log::err(format!("animation {:?}, is missing", self.curr));
"<<error !!>>".to_string()
}
},
duration: self.get_duration_at_index(self.curr),
})
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Anim {
pub name: String,
pub duration: f32,
}
impl<'a> Anims<'a> {
pub fn from<M: AsRef<Model>>(model: &'a M) -> Anims<'a> {
Anims { model: model.as_ref(), curr: -1 }
}
pub fn get_name_at_index(&self, index: i32) -> Option<&str> {
unsafe {
if model_anim_count(self.model.0.as_ptr()) > index {
CStr::from_ptr(model_anim_get_name(self.model.0.as_ptr(), index)).to_str().ok()
} else {
None
}
}
}
pub fn get_duration_at_index(&self, index: i32) -> f32 {
unsafe {
if model_anim_count(self.model.0.as_ptr()) > index {
model_anim_get_duration(self.model.0.as_ptr(), index)
} else {
-0.01
}
}
}
pub fn step_anim(&mut self) -> &mut Self {
unsafe { model_step_anim(self.model.0.as_ptr()) };
self
}
pub fn play_anim(&mut self, animation_name: impl AsRef<str>, mode: AnimMode) -> &mut Self {
let c_str = CString::new(animation_name.as_ref()).unwrap();
unsafe { model_play_anim(self.model.0.as_ptr(), c_str.as_ptr(), mode) };
self
}
pub fn play_anim_idx(&mut self, idx: i32, mode: AnimMode) -> &mut Self {
unsafe { model_play_anim_idx(self.model.0.as_ptr(), idx, mode) };
self
}
pub fn anim_time(&mut self, time: f32) -> &mut Self {
unsafe { model_set_anim_time(self.model.0.as_ptr(), time) };
self
}
pub fn anim_completion(&mut self, percent: f32) -> &mut Self {
unsafe { model_set_anim_completion(self.model.0.as_ptr(), percent) };
self
}
pub fn find_anim<S: AsRef<str>>(&self, name: S) -> Option<i32> {
let c_str = match CString::new(name.as_ref()) {
Ok(c_str) => c_str,
Err(..) => return None,
};
let index = unsafe { model_anim_find(self.model.0.as_ptr(), c_str.as_ptr()) };
if index < 0 { None } else { Some(index) }
}
pub fn get_count(&self) -> i32 {
unsafe { model_anim_count(self.model.0.as_ptr()) }
}
pub fn get_active_anim(&self) -> i32 {
unsafe { model_anim_active(self.model.0.as_ptr()) }
}
pub fn get_anim_mode(&self) -> AnimMode {
unsafe { model_anim_active_mode(self.model.0.as_ptr()) }
}
pub fn get_anim_time(&self) -> f32 {
unsafe { model_anim_active_time(self.model.0.as_ptr()) }
}
pub fn get_anim_completion(&self) -> f32 {
unsafe { model_anim_active_completion(self.model.0.as_ptr()) }
}
}
#[derive(Debug, Copy, Clone)]
pub struct Nodes<'a> {
model: &'a Model,
}
unsafe extern "C" {
pub fn model_subset_count(model: ModelT) -> i32;
pub fn model_node_add(
model: ModelT,
name: *const c_char,
model_transform: Matrix,
mesh: MeshT,
material: MaterialT,
solid: Bool32T,
) -> ModelNodeId;
pub fn model_node_add_child(
model: ModelT,
parent: ModelNodeId,
name: *const c_char,
local_transform: Matrix,
mesh: MeshT,
material: MaterialT,
solid: Bool32T,
) -> ModelNodeId;
pub fn model_node_find(model: ModelT, name: *const c_char) -> ModelNodeId;
pub fn model_node_sibling(model: ModelT, node: ModelNodeId) -> ModelNodeId;
pub fn model_node_parent(model: ModelT, node: ModelNodeId) -> ModelNodeId;
pub fn model_node_child(model: ModelT, node: ModelNodeId) -> ModelNodeId;
pub fn model_node_count(model: ModelT) -> i32;
pub fn model_node_index(model: ModelT, index: i32) -> ModelNodeId;
pub fn model_node_visual_count(model: ModelT) -> i32;
pub fn model_node_visual_index(model: ModelT, index: i32) -> ModelNodeId;
pub fn model_node_iterate(model: ModelT, node: ModelNodeId) -> ModelNodeId;
pub fn model_node_get_root(model: ModelT) -> ModelNodeId;
pub fn model_node_get_name(model: ModelT, node: ModelNodeId) -> *const c_char;
pub fn model_node_get_solid(model: ModelT, node: ModelNodeId) -> Bool32T;
pub fn model_node_get_visible(model: ModelT, node: ModelNodeId) -> Bool32T;
pub fn model_node_get_material(model: ModelT, node: ModelNodeId) -> MaterialT;
pub fn model_node_get_mesh(model: ModelT, node: ModelNodeId) -> MeshT;
pub fn model_node_get_transform_model(model: ModelT, node: ModelNodeId) -> Matrix;
pub fn model_node_get_transform_local(model: ModelT, node: ModelNodeId) -> Matrix;
pub fn model_node_set_name(model: ModelT, node: ModelNodeId, name: *const c_char);
pub fn model_node_set_solid(model: ModelT, node: ModelNodeId, solid: Bool32T);
pub fn model_node_set_visible(model: ModelT, node: ModelNodeId, visible: Bool32T);
pub fn model_node_set_material(model: ModelT, node: ModelNodeId, material: MaterialT);
pub fn model_node_set_mesh(model: ModelT, node: ModelNodeId, mesh: MeshT);
pub fn model_node_set_transform_model(model: ModelT, node: ModelNodeId, transform_model_space: Matrix);
pub fn model_node_set_transform_local(model: ModelT, node: ModelNodeId, transform_local_space: Matrix);
pub fn model_node_info_get(model: ModelT, node: ModelNodeId, info_key_utf8: *const c_char) -> *mut c_char;
pub fn model_node_info_set(
model: ModelT,
node: ModelNodeId,
info_key_utf8: *const c_char,
info_value_utf8: *const c_char,
);
pub fn model_node_info_remove(model: ModelT, node: ModelNodeId, info_key_utf8: *const c_char) -> Bool32T;
pub fn model_node_info_clear(model: ModelT, node: ModelNodeId);
pub fn model_node_info_count(model: ModelT, node: ModelNodeId) -> i32;
pub fn model_node_info_iterate(
model: ModelT,
node: ModelNodeId,
ref_iterator: *mut i32,
out_key_utf8: *mut *const c_char,
out_value_utf8: *mut *const c_char,
) -> Bool32T;
}
#[derive(Debug, Copy, Clone)]
pub struct NodeIter<'a> {
model: &'a Model,
index: i32,
visual: bool,
}
impl<'a> Iterator for NodeIter<'a> {
type Item = ModelNode<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.index += 1;
if self.visual {
let count = unsafe { model_node_visual_count(self.model.0.as_ptr()) };
if self.index < count {
match unsafe { model_node_visual_index(self.model.0.as_ptr(), self.index) } {
-1 => {
Log::err(format!(
"node at index {:?}, is missing when {:?} visual nodes are expected for Model {:?}",
self.index,
count,
self.model.get_id()
));
None
}
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
} else {
None
}
} else {
let count = unsafe { model_node_count(self.model.0.as_ptr()) };
if self.index < count {
match unsafe { model_node_index(self.model.0.as_ptr(), self.index) } {
-1 => {
Log::err(format!(
"node at index {:?}, is missing when {:?} visual nodes are expected for Model {:?}",
self.index,
count,
self.model.get_id()
));
None
}
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
} else {
None
}
}
}
}
impl<'a> NodeIter<'a> {
pub fn all_from(model: &'a impl AsRef<Model>) -> NodeIter<'a> {
NodeIter { index: -1, model: model.as_ref(), visual: false }
}
pub fn visuals_from(model: &'a impl AsRef<Model>) -> NodeIter<'a> {
NodeIter { index: -1, model: model.as_ref(), visual: true }
}
}
impl<'a> Nodes<'a> {
pub fn from(model: &'a impl AsRef<Model>) -> Nodes<'a> {
Nodes { model: model.as_ref() }
}
pub fn add<S: AsRef<str>>(
&mut self,
name: S,
local_transform: impl Into<Matrix>,
mesh: Option<&Mesh>,
material: Option<&Material>,
solid: bool,
) -> &mut Self {
let c_str = CString::new(name.as_ref()).unwrap();
let mesh = match mesh {
Some(mesh) => mesh.0.as_ptr(),
None => null_mut(),
};
let material = match material {
Some(material) => material.0.as_ptr(),
None => null_mut(),
};
unsafe {
model_node_add(
self.model.0.as_ptr(),
c_str.as_ptr(),
local_transform.into(),
mesh,
material,
solid as Bool32T,
)
};
self
}
pub fn all(&'_ self) -> NodeIter<'_> {
NodeIter::all_from(self.model)
}
pub fn visuals(&'_ self) -> NodeIter<'_> {
NodeIter::visuals_from(self.model)
}
pub fn find<S: AsRef<str>>(&'_ self, name: S) -> Option<ModelNode<'_>> {
let c_str = CString::new(name.as_ref()).unwrap();
match unsafe { model_node_find(self.model.0.as_ptr(), c_str.as_ptr()) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_count(&self) -> i32 {
unsafe { model_node_count(self.model.0.as_ptr()) }
}
pub fn get_visual_count(&self) -> i32 {
unsafe { model_node_visual_count(self.model.0.as_ptr()) }
}
pub fn get_index(&'_ self, index: i32) -> Option<ModelNode<'_>> {
if unsafe { model_node_count(self.model.0.as_ptr()) } <= index {
return None;
}
match unsafe { model_node_index(self.model.0.as_ptr(), index) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_visual_index(&'_ self, index: i32) -> Option<ModelNode<'_>> {
if unsafe { model_node_visual_count(self.model.0.as_ptr()) } <= index {
return None;
}
match unsafe { model_node_visual_index(self.model.0.as_ptr(), index) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_root_node(&'_ self) -> Option<ModelNode<'_>> {
let id = unsafe { model_node_get_root(self.model.0.as_ptr()) };
if id == -1 { None } else { Some(ModelNode { model: self.model, id }) }
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ModelNode<'a> {
model: &'a Model,
id: ModelNodeId,
}
pub type ModelNodeId = i32;
impl ModelNode<'_> {
pub fn name<S: AsRef<str>>(&mut self, name: S) -> &mut Self {
let c_str = CString::new(name.as_ref()).unwrap();
unsafe { model_node_set_name(self.model.0.as_ptr(), self.id, c_str.as_ptr()) };
self
}
pub fn solid(&mut self, solid: bool) -> &mut Self {
unsafe { model_node_set_solid(self.model.0.as_ptr(), self.id, solid as Bool32T) };
self
}
pub fn visible(&mut self, visible: bool) -> &mut Self {
unsafe { model_node_set_visible(self.model.0.as_ptr(), self.id, visible as Bool32T) };
self
}
pub fn material<M: AsRef<Material>>(&mut self, material: M) -> &mut Self {
unsafe { model_node_set_material(self.model.0.as_ptr(), self.id, material.as_ref().0.as_ptr()) };
self
}
pub fn mesh<M: AsRef<Mesh>>(&mut self, mesh: M) -> &mut Self {
unsafe { model_node_set_mesh(self.model.0.as_ptr(), self.id, mesh.as_ref().0.as_ptr()) };
self
}
pub fn model_transform(&mut self, transform_model_space: impl Into<Matrix>) -> &mut Self {
unsafe { model_node_set_transform_model(self.model.0.as_ptr(), self.id, transform_model_space.into()) };
self
}
pub fn local_transform(&mut self, transform_model_space: impl Into<Matrix>) -> &mut Self {
unsafe { model_node_set_transform_local(self.model.0.as_ptr(), self.id, transform_model_space.into()) };
self
}
pub fn add_child<S: AsRef<str>>(
&mut self,
name: S,
local_transform: impl Into<Matrix>,
mesh: Option<&Mesh>,
material: Option<&Material>,
solid: bool,
) -> &mut Self {
let c_str = CString::new(name.as_ref()).unwrap();
let mesh = match mesh {
Some(mesh) => mesh.0.as_ptr(),
None => null_mut(),
};
let material = match material {
Some(material) => material.0.as_ptr(),
None => null_mut(),
};
unsafe {
model_node_add_child(
self.model.0.as_ptr(),
self.id,
c_str.as_ptr(),
local_transform.into(),
mesh,
material,
solid as Bool32T,
)
};
self
}
pub fn get_id(&self) -> ModelNodeId {
self.id
}
pub fn get_name(&self) -> Option<&str> {
unsafe { CStr::from_ptr(model_node_get_name(self.model.0.as_ptr(), self.id)).to_str().ok() }
}
pub fn get_solid(&self) -> bool {
unsafe { model_node_get_solid(self.model.0.as_ptr(), self.id) != 0 }
}
pub fn get_visible(&self) -> bool {
unsafe { model_node_get_visible(self.model.0.as_ptr(), self.id) != 0 }
}
pub fn get_material(&self) -> Option<Material> {
NonNull::new(unsafe { model_node_get_material(self.model.0.as_ptr(), self.id) }).map(Material)
}
pub fn get_mesh(&self) -> Option<Mesh> {
NonNull::new(unsafe { model_node_get_mesh(self.model.0.as_ptr(), self.id) }).map(Mesh)
}
pub fn get_model_transform(&self) -> Matrix {
unsafe { model_node_get_transform_model(self.model.0.as_ptr(), self.id) }
}
pub fn get_local_transform(&self) -> Matrix {
unsafe { model_node_get_transform_local(self.model.0.as_ptr(), self.id) }
}
pub fn iterate(&'_ self) -> Option<ModelNode<'_>> {
match unsafe { model_node_iterate(self.model.0.as_ptr(), self.id) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_child(&'_ self) -> Option<ModelNode<'_>> {
match unsafe { model_node_child(self.model.0.as_ptr(), self.id) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_sibling(&'_ self) -> Option<ModelNode<'_>> {
match unsafe { model_node_sibling(self.model.0.as_ptr(), self.id) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_parent(&'_ self) -> Option<ModelNode<'_>> {
match unsafe { model_node_parent(self.model.0.as_ptr(), self.id) } {
-1 => None,
otherwise => Some(ModelNode { model: self.model, id: otherwise }),
}
}
pub fn get_next(&'_ self) -> Option<ModelNode<'_>> {
self.iterate()
}
pub fn get_model(&self) -> &Model {
self.model
}
pub fn get_infos(&'_ self) -> Infos<'_> {
Infos::from(self)
}
}
pub struct Infos<'a> {
model: &'a Model,
node_id: ModelNodeId,
curr: i32,
}
impl Iterator for Infos<'_> {
type Item = Info;
fn next(&mut self) -> Option<Self::Item> {
if let Some(res) = Infos::info_iterate(self.model, self.curr, self.node_id) {
self.curr = res.2;
Some(Info { name: res.0.to_string(), value: res.1.to_string() })
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Info {
pub name: String,
pub value: String,
}
impl<'a> Infos<'a> {
pub fn from(node: &'a ModelNode) -> Infos<'a> {
Infos { model: node.model, node_id: node.id, curr: 0 }
}
fn info_iterate(model: &Model, mut iterator: i32, node: ModelNodeId) -> Option<(&str, &str, i32)> {
let out_key_utf8 = CString::new("H").unwrap().into_raw() as *mut *const c_char;
let out_value_utf8 = CString::new("H").unwrap().into_raw() as *mut *const c_char;
let ref_iterator = &mut iterator as *mut i32;
unsafe {
let res = model_node_info_iterate(model.0.as_ptr(), node, ref_iterator, out_key_utf8, out_value_utf8);
if res != 0 {
let key = CStr::from_ptr(*out_key_utf8);
let value = CStr::from_ptr(*out_value_utf8);
Some((key.to_str().unwrap(), value.to_str().unwrap(), *ref_iterator))
} else {
None
}
}
}
pub fn clear(&mut self) -> &mut Self {
unsafe { model_node_info_clear(self.model.0.as_ptr(), self.node_id) };
self
}
pub fn remove_info<S: AsRef<str>>(&mut self, info_key_utf8: S) -> &mut Self {
let c_str = CString::new(info_key_utf8.as_ref()).unwrap();
unsafe {
if model_node_info_remove(self.model.0.as_ptr(), self.node_id, c_str.as_ptr()) == 0 {
Log::err(format!("Info {:?} was not found during remove", info_key_utf8.as_ref()));
}
}
self
}
pub fn set_info<S: AsRef<str>>(&mut self, info_key_utf8: S, info_value_utf8: S) -> &mut Self {
let c_str = CString::new(info_key_utf8.as_ref()).unwrap();
let c_value = CString::new(info_value_utf8.as_ref()).unwrap();
unsafe { model_node_info_set(self.model.0.as_ptr(), self.node_id, c_str.as_ptr(), c_value.as_ptr()) };
self
}
pub fn get_info<S: AsRef<str>>(&self, info_key_utf8: S) -> Option<&str> {
let c_str = CString::new(info_key_utf8.as_ref()).unwrap();
match NonNull::new(unsafe { model_node_info_get(self.model.0.as_ptr(), self.node_id, c_str.as_ptr()) }) {
Some(non_null) => unsafe { CStr::from_ptr(non_null.as_ref()).to_str().ok() },
None => None,
}
}
pub fn contains<S: AsRef<str>>(&self, info_key_utf8: S) -> bool {
self.get_info(info_key_utf8).is_some()
}
pub fn get_count(&self) -> i32 {
unsafe { model_node_info_count(self.model.0.as_ptr(), self.node_id) }
}
}