apple-apis 0.1.0

System bindings for Apple APIs
use serde::Deserialize;
use std::env::var;
use std::process::Command;

fn main() {
	// don't do any of this when running in docs.rs
	#[cfg(docsrs)]
	return;

	let profile = var("PROFILE").unwrap();
	let out_dir = var("OUT_DIR").unwrap();

	let info = SwiftTargetInfo::get();
	let unversioned_triple = &*info.target.unversioned_triple;

	println!("cargo::rerun-if-changed=Package.swift");
	println!("cargo::rustc-link-search=native={out_dir}/{unversioned_triple}/{profile}");

	// linking swift itself
	for rt_lib_path in &info.paths.runtime_library_paths {
		println!("cargo::rustc-link-search=native={}", &**rt_lib_path);
	}

	#[allow(unused_mut)]
	let mut traits = Vec::<&str>::new();

	macro_rules! link_pkgs {
		{ $($name:literal)* } => {
			$(
				#[cfg(feature = $name)] {
					println!(concat!("cargo::rerun-if-changed=src/", $name));
					traits.push($name);
				}
			)*
		}
	}

	link_pkgs! {
		"accessibility"
		"dispatch"
		"pushkit"
		"swift"
	}

	let traits = traits.join(",");
	let has_traits = traits.is_empty();
	let traits = &["--traits", &*traits] as &[_];

	let success = Command::new("swift")
		.arg("build")
		.arg("-c")
		.arg(&*profile)
		.arg("--build-path")
		.arg(&*out_dir)
		.arg("--disable-default-traits")
		.arg("--product")
		.arg("apple-apis")
		.args(if !has_traits { traits } else { &[] })
		.status()
		.map(|res| res.success());

	let Ok(true) = success else {
		panic!("failed to build swift");
	};

	println!("cargo::rustc-link-lib=static:+whole-archive=apple-apis");
}

#[derive(Deserialize)]
struct SwiftTargetInfo {
	// #[serde(rename = "compilerVersion")]
	// compiler_version: String,
	// #[serde(rename = "swiftCompilerTag")]
	// swift_compiler_tag: String,
	#[allow(dead_code)]
	target: SwiftTargetInfoTarget,
	paths: SwiftTargetInfoPaths
}

#[derive(Deserialize)]
struct SwiftTargetInfoTarget {
	// triple: String,
	#[allow(dead_code)]
	#[serde(rename = "unversionedTriple")]
	unversioned_triple: String,
	// #[serde(rename = "moduleTriple")]
	// module_triple: String,
	// platform: String,
	// arch: String,
	// #[serde(rename = "pointerWidthInBits")]
	// pointer_width_in_bits: u8,
	// #[serde(rename = "pointerWidthInBytes")]
	// pointer_width_in_bytes: u8,
	// #[serde(rename = "swiftRuntimeCompatibilityVersion")]
	// swift_runtime_compatibility_version: String,
	// #[serde(rename = "compatibilityLibraries")]
	// compatibility_libraries: Vec<String>,
	// #[serde(rename = "openbsdBTCFIEnabled")]
	// openbsd_btcfi_enabled: bool,
	// #[serde(rename = "librariesRequireRPath")]
	// libraries_require_rpath: bool
}

#[derive(Deserialize)]
struct SwiftTargetInfoPaths {
	#[serde(rename = "runtimeLibraryPaths")]
	runtime_library_paths: Vec<String>,
	// #[serde(rename = "runtimeLibraryImportPaths")]
	// runtime_library_import_paths: Vec<String>,
	// #[serde(rename = "runtimeResourcePath")]
	// runtime_resource_path: String
}

impl SwiftTargetInfo {
	fn get() -> Self {
		let info = Command::new("swift")
			.arg("-print-target-info")
			.output()
			.unwrap()
			.stdout;

		serde_json::from_slice(&info).unwrap()
	}
}