apple-apis 0.1.0

System bindings for Apple APIs
use crate::util::*;

use crate::reprc::ReprCBoolU32;
use core::convert::Infallible;
use core::fmt::{ self, Debug, Display, Write as _ };
use core::str::FromStr;

sys! {
	"swift"

	pub fn apple_apis_swift_string_drop(ptr: Ptr);
	pub fn apple_apis_swift_string_new() -> Ptr;
	pub fn apple_apis_swift_string_extend(
		ptr: Ptr,
		iter: Ptr,
		len: usize,
		advance_iter: extern "C" fn(Ptr) -> ReprCBoolU32
	);
	pub fn apple_apis_swift_string_iter_chars(
		ptr: Ptr,
		opaque_this: Ptr,
		next_char: extern "C" fn(Ptr, u32)
	);
}

wrapper! {
	pub struct String;
	new: sys::apple_apis_swift_string_new;
	drop: sys::apple_apis_swift_string_drop;
}

impl String {
	#[allow(clippy::should_implement_trait)]
	pub fn from_str(s: &str) -> Self {
		let string = String::new();
		string.push_str(s);
		string
	}

	pub fn push_str(&self, string: &str) {
		// mmmmmaybe detect feature = "std" and then just send over a cstring lmao

		use core::str::Chars;

		let len = string.len();
		let mut iter: Chars<'_> = string.chars();

		extern "C" fn advance_iter(ptr: Ptr) -> ReprCBoolU32 {
			let iter = unsafe { &mut *ptr.cast::<Chars<'_>>() };

			match iter.next() {
				Some(c) => { ReprCBoolU32 { val_bool: true, val_u32: c as _ } }
				None => { ReprCBoolU32 { val_bool: false, val_u32: 0 } }
			}
		}

		unsafe {
			sys::apple_apis_swift_string_extend(
				self.as_ptr(),
				(&raw mut iter).cast(),
				len,
				advance_iter
			);
		}
	}
}

impl Debug for String {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		// this could be better.. rust std String does \" alongside other sorts
		// of debug things. ideally we get something from swift we can make into
		// a rust `&str`, but I don't think that's happening

		f.write_char('"')?;
		Display::fmt(&self, f)?;
		f.write_char('"')?;

		Ok(())
	}
}

impl Display for String {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		struct Data<'f, 'h> {
			f: &'f mut fmt::Formatter<'h>,
			result: Result<(), fmt::Error>
		}

		extern "C" fn next_char(opaque_this: Ptr, char: u32) {
			// SAFETY: assuming swift behaves
			let char = unsafe { char::try_from(char).unwrap_unchecked() };
			// SAFETY: assuming swift behaves
			let Data { f, result } = unsafe { &mut *opaque_this.cast::<Data<'_, '_>>() };

			*result = result.and_then(|_| f.write_char(char))
		}

		let mut data = Data { f, result: Ok(()) };

		unsafe {
			sys::apple_apis_swift_string_iter_chars(
				self.as_ptr(),
				(&raw mut data).cast(),
				next_char
			)
		}

		data.result
	}
}

impl FromStr for String {
	type Err = Infallible;

	fn from_str(s: &str) -> Result<String, Infallible> {
		Ok(Self::from_str(s))
	}
}