use crate::{StereoKitError, system::IAsset, tex::TexT};
use std::{
ffi::{CStr, CString, c_char},
path::Path,
ptr::NonNull,
};
#[repr(C)]
#[derive(Debug)]
pub struct Font(pub NonNull<_FontT>);
impl Drop for Font {
fn drop(&mut self) {
unsafe { font_release(self.0.as_ptr()) };
}
}
impl AsRef<Font> for Font {
fn as_ref(&self) -> &Font {
self
}
}
#[repr(C)]
#[derive(Debug)]
pub struct _FontT {
_unused: [u8; 0],
}
pub type FontT = *mut _FontT;
unsafe extern "C" {
pub fn font_find(id: *const c_char) -> FontT;
pub fn font_create(file_utf8: *const c_char) -> FontT;
pub fn font_create_files(in_arr_files: *mut *const c_char, file_count: i32) -> FontT;
pub fn font_create_family(font_family: *const c_char) -> FontT;
pub fn font_set_id(font: FontT, id: *const c_char);
pub fn font_get_id(font: FontT) -> *const c_char;
pub fn font_addref(font: FontT);
pub fn font_release(font: FontT);
pub fn font_get_tex(font: FontT) -> TexT;
}
impl IAsset for Font {
fn get_id(&self) -> &str {
self.get_id()
}
}
impl Default for Font {
fn default() -> Self {
let c_str = CString::new("default/font").unwrap();
Font(NonNull::new(unsafe { font_find(c_str.as_ptr()) }).unwrap())
}
}
impl Font {
pub fn from_file(file_utf8: impl AsRef<Path>) -> Result<Font, StereoKitError> {
let path_buf = file_utf8.as_ref().to_path_buf();
let c_str = CString::new(
path_buf
.clone()
.to_str()
.ok_or(StereoKitError::FontFile(path_buf.clone(), "CString conversion".to_string()))?,
)?;
Ok(Font(
NonNull::new(unsafe { font_create(c_str.as_ptr()) })
.ok_or(StereoKitError::FontFile(path_buf, "font_create failed".to_string()))?,
))
}
pub fn from_files<P: AsRef<Path>>(files_utf8: &[P]) -> Result<Font, StereoKitError> {
let mut c_files = Vec::new();
for path in files_utf8 {
let path = path.as_ref();
let c_str = CString::new(path.to_str().ok_or(StereoKitError::FontFiles(
path.to_str().unwrap().to_string(),
"CString conversion".to_string(),
))?)?;
c_files.push(c_str);
}
let mut c_files_ptr = Vec::new();
for str in c_files.iter() {
c_files_ptr.push(str.as_ptr());
}
let in_arr_files_cstr = c_files_ptr.as_mut_slice().as_mut_ptr();
Ok(Font(NonNull::new(unsafe { font_create_files(in_arr_files_cstr, files_utf8.len() as i32) }).ok_or(
StereoKitError::FontFiles("many files".to_owned(), "font_create_files failed".to_string()),
)?))
}
pub fn from_family(font_family: impl AsRef<str>) -> Result<Font, StereoKitError> {
let c_str = CString::new(font_family.as_ref()).map_err(|_| {
StereoKitError::FontFamily(font_family.as_ref().into(), "CString conversion error".to_string())
})?;
Ok(Font(NonNull::new(unsafe { font_create_family(c_str.as_ptr()) }).ok_or(
StereoKitError::FontFamily(font_family.as_ref().into(), "font_create_family failed".to_string()),
)?))
}
pub fn find<S: AsRef<str>>(id: S) -> Result<Font, StereoKitError> {
let c_str = CString::new(id.as_ref())
.map_err(|_| StereoKitError::FontFind(id.as_ref().into(), "CString conversion error".to_string()))?;
Ok(Font(
NonNull::new(unsafe { font_find(c_str.as_ptr()) })
.ok_or(StereoKitError::FontFind(id.as_ref().into(), "font_find failed".to_string()))?,
))
}
pub fn clone_ref(&self) -> Font {
Font(NonNull::new(unsafe { font_find(font_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 { font_set_id(self.0.as_ptr(), c_str.as_ptr()) };
self
}
pub fn get_id(&self) -> &str {
unsafe { CStr::from_ptr(font_get_id(self.0.as_ptr())) }.to_str().unwrap()
}
}