diff --git a/Cargo.lock b/Cargo.lock index 4d6c06b..1d7418a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,7 +511,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.10.21" +version = "1.11.0" dependencies = [ "aes-gcm-stream", "authenticator 0.3.1", @@ -549,7 +549,7 @@ dependencies = [ "spki 0.7.3", "ssh-agent", "sshcerts", - "swift-rs", + "swift-secure-enclave-tool-rs", "tabled", "u2f", "x509", @@ -3688,14 +3688,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] -name = "swift-rs" -version = "1.0.7" +name = "swift-secure-enclave-tool-rs" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +checksum = "1cc8ba06af10bf59b2f47fb46238a0c62c2ac927184253b4fb998b3b3f68950a" dependencies = [ - "base64 0.21.7", - "serde", - "serde_json", + "base64 0.22.1", + "hex", + "rust_util", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2cca125..1270d9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,12 @@ [package] name = "card-cli" -version = "1.10.21" +version = "1.11.0" authors = ["Hatter Jiang "] edition = "2018" [features] -default = ["with-sequoia-openpgp", "with-secure-enclave"] +default = ["with-sequoia-openpgp"] with-sequoia-openpgp = ["sequoia-openpgp", "openpgp-card-sequoia"] -with-secure-enclave = ["swift-rs"] [dependencies] authenticator = "0.3" @@ -50,15 +49,12 @@ rpassword = "7.3" secrecy = "0.8" der-parser = "9.0" sshcerts = "0.13" -swift-rs = { version = "1.0.7", optional = true } regex = "1.4.6" aes-gcm-stream = "0.2.4" +swift-secure-enclave-tool-rs = "0.1.0" #lazy_static = "1.4.0" #ssh-key = "0.4.0" #ctap-hid-fido2 = "2.1.3" -[build-dependencies] -swift-rs = { version = "1.0.7", features = ["build"], optional = true } - [patch.crates-io] u2f = { git = "https://github.com/jht5945/u2f-rs.git" } diff --git a/build.rs b/build.rs deleted file mode 100644 index 2640416..0000000 --- a/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - // Ensure this matches the versions set in your `Package.swift` file. - #[cfg(feature = "with-secure-enclave")] - swift_rs::SwiftLinker::new("11") - .with_ios("11") - .with_package("swift-lib", "./swift-lib/") - .link(); -} - diff --git a/src/cmd_generatekeypair.rs b/src/cmd_generatekeypair.rs index 33e9cb4..a16fc1a 100644 --- a/src/cmd_generatekeypair.rs +++ b/src/cmd_generatekeypair.rs @@ -1,11 +1,7 @@ use crate::ecdsautil; -use crate::keyutil::{parse_key_uri, KeyUri}; use clap::{App, Arg, ArgMatches, SubCommand}; -use p256::elliptic_curve::sec1::FromEncodedPoint; -use p256::{EncodedPoint, PublicKey}; use rust_util::util_clap::{Command, CommandError}; use rust_util::util_msg; -use spki::EncodePublicKey; use std::collections::BTreeMap; pub struct CommandImpl; diff --git a/src/cmd_hmacdecrypt.rs b/src/cmd_hmacdecrypt.rs index 05e356a..d702294 100644 --- a/src/cmd_hmacdecrypt.rs +++ b/src/cmd_hmacdecrypt.rs @@ -31,7 +31,7 @@ impl Command for CommandImpl { } let ciphertext = sub_arg_matches.value_of("ciphertext").unwrap(); - let plaintext = hmacutil::hmac_decrypt_to_string(&ciphertext)?; + let plaintext = hmacutil::hmac_decrypt_to_string(ciphertext)?; if json_output { let mut json = BTreeMap::<&'_ str, String>::new(); diff --git a/src/cmd_parseecdsasignature.rs b/src/cmd_parseecdsasignature.rs index f428e7e..927b27e 100644 --- a/src/cmd_parseecdsasignature.rs +++ b/src/cmd_parseecdsasignature.rs @@ -8,8 +8,6 @@ use rust_util::util_msg; use crate::ecdsautil::parse_ecdsa_r_and_s; use crate::util::try_decode; -const SEPARATOR: &str = "."; - pub struct CommandImpl; impl Command for CommandImpl { diff --git a/src/cmd_se_ecdh.rs b/src/cmd_se_ecdh.rs index 38d55fb..449d84c 100644 --- a/src/cmd_se_ecdh.rs +++ b/src/cmd_se_ecdh.rs @@ -50,8 +50,7 @@ impl Command for CommandImpl { let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?; debugging!("Secure enclave key URI: {:?}", se_key_uri); - let ephemeral_public_key_der_bytes; - if epk.starts_with("04") { + let ephemeral_public_key_der_bytes = if epk.starts_with("04") { let ephemeral_public_key_point_bytes = opt_result!( hex::decode(epk), "Decode public key point from hex failed: {}" @@ -65,11 +64,10 @@ impl Command for CommandImpl { return simple_error!("Parse public key failed."); } let public_key = public_key_opt.unwrap(); - ephemeral_public_key_der_bytes = public_key.to_public_key_der()?.as_bytes().to_vec(); + public_key.to_public_key_der()?.as_bytes().to_vec() } else { - ephemeral_public_key_der_bytes = - opt_result!(hex::decode(epk), "Decode public key from hex failed: {}"); - } + opt_result!(hex::decode(epk), "Decode public key from hex failed: {}") + }; let dh = seutil::secure_enclave_p256_dh( &se_key_uri.private_key, diff --git a/src/cmd_signjwtsoft.rs b/src/cmd_signjwtsoft.rs index 405c042..2a1ebf5 100644 --- a/src/cmd_signjwtsoft.rs +++ b/src/cmd_signjwtsoft.rs @@ -7,11 +7,8 @@ use jwt::{AlgorithmType, Header, ToBase64}; use rust_util::util_clap::{Command, CommandError}; use rust_util::{util_msg, util_time, XResult}; use serde_json::{Map, Number, Value}; -use yubikey::piv::{sign_data, AlgorithmId, SlotId}; -use yubikey::{Certificate, YubiKey}; -use crate::ecdsautil::parse_ecdsa_to_rs; -use crate::{cmd_signjwt, digest, ecdsautil, hmacutil, pivutil, rsautil, util}; +use crate::{cmd_signjwt, digest, ecdsautil, hmacutil, rsautil, util}; const SEPARATOR: &str = "."; diff --git a/src/ecdsautil.rs b/src/ecdsautil.rs index 3b3b345..c121f3f 100644 --- a/src/ecdsautil.rs +++ b/src/ecdsautil.rs @@ -22,8 +22,8 @@ pub fn parse_ecdsa_to_rs(signature_der: &[u8]) -> XResult> { } pub fn parse_ecdsa_r_and_s(signature_der: &[u8]) -> XResult<(Vec, Vec)> { - let mut vec_r: Vec = Vec::new(); - let mut vec_s: Vec = Vec::new(); + let vec_r: Vec; + let vec_s: Vec; let (_, parsed_signature) = opt_result!(der_parser::parse_der(signature_der), "Parse signature failed: {}"); match parsed_signature.content { BerObjectContent::Sequence(seq) => { @@ -73,7 +73,6 @@ pub fn generate_p384_keypair() -> XResult<(String, String, String)> { macro_rules! parse_ecdsa_private_key { ($algo: tt, $parse_ecdsa_private_key: tt) => ({ - use $algo::ecdsa::{SigningKey}; use $algo::pkcs8::DecodePrivateKey; use $algo::SecretKey; @@ -103,7 +102,7 @@ pub fn sign_p256_rs(private_key_d: &[u8], pre_hash: &[u8]) -> XResult> { use p256::ecdsa::{SigningKey, Signature}; use p256::ecdsa::signature::hazmat::PrehashSigner; - let signing_key = SigningKey::from_slice(&private_key_d)?; + let signing_key = SigningKey::from_slice(private_key_d)?; let signature: Signature = signing_key.sign_prehash(pre_hash)?; Ok(signature.to_bytes().to_vec()) @@ -113,7 +112,7 @@ pub fn sign_p384_rs(private_key_d: &[u8], pre_hash: &[u8]) -> XResult> { use p384::ecdsa::{SigningKey, Signature}; use p384::ecdsa::signature::hazmat::PrehashSigner; - let signing_key = SigningKey::from_slice(&private_key_d)?; + let signing_key = SigningKey::from_slice(private_key_d)?; let signature: Signature = signing_key.sign_prehash(pre_hash)?; Ok(signature.to_bytes().to_vec()) diff --git a/src/hmacutil.rs b/src/hmacutil.rs index 0855d89..64ca364 100644 --- a/src/hmacutil.rs +++ b/src/hmacutil.rs @@ -8,7 +8,7 @@ use yubico_manager::config::{Config, Mode, Slot}; use yubico_manager::hmacmode::HmacKey; use yubico_manager::sec::hmac_sha1; use yubico_manager::Yubico; -use crate::digest::{copy_sha256, sha256, sha256_bytes}; +use crate::digest::{copy_sha256, sha256_bytes}; use crate::util::{base64_decode, base64_encode}; const HMAC_ENC_PREFIX: &str = "hmac_enc:"; @@ -33,8 +33,8 @@ pub fn hmac_encrypt(plaintext: &[u8]) -> XResult { Ok(format!("{}{}:{}:{}", HMAC_ENC_PREFIX, - hex::encode(&hmac_nonce), - hex::encode(&aes_gcm_nonce), + hex::encode(hmac_nonce), + hex::encode(aes_gcm_nonce), base64_encode(&ciphertext) )) } @@ -92,7 +92,7 @@ pub fn compute_yubikey_hmac(challenge_bytes: &[u8]) -> XResult> { .set_mode(Mode::Sha1) .set_slot(Slot::Slot2); - let hmac_result = opt_result!(yubi.challenge_response_hmac(&challenge_bytes, config), "Challenge HMAC failed: {}"); + let hmac_result = opt_result!(yubi.challenge_response_hmac(challenge_bytes, config), "Challenge HMAC failed: {}"); Ok(hmac_result.deref().to_vec()) } diff --git a/src/main.rs b/src/main.rs index 93cd1e3..585c5f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,15 +33,10 @@ mod cmd_pivverify; mod cmd_rsadecrypt; mod cmd_rsaencrypt; mod cmd_rsaverify; -#[cfg(feature = "with-secure-enclave")] mod cmd_se; -#[cfg(feature = "with-secure-enclave")] mod cmd_se_ecdh; -#[cfg(feature = "with-secure-enclave")] mod cmd_se_ecsign; -#[cfg(feature = "with-secure-enclave")] mod cmd_se_generate; -#[cfg(feature = "with-secure-enclave")] mod cmd_se_recover; mod cmd_signfile; mod cmd_signjwt; @@ -68,7 +63,6 @@ mod pinutil; mod pivutil; mod pkiutil; mod rsautil; -#[cfg(feature = "with-secure-enclave")] mod seutil; mod signfile; mod sshutil; @@ -139,15 +133,10 @@ fn inner_main() -> CommandError { Box::new(cmd_signjwtsoft::CommandImpl), Box::new(cmd_signfile::CommandImpl), Box::new(cmd_verifyfile::CommandImpl), - #[cfg(feature = "with-secure-enclave")] Box::new(cmd_se::CommandImpl), - #[cfg(feature = "with-secure-enclave")] Box::new(cmd_se_generate::CommandImpl), - #[cfg(feature = "with-secure-enclave")] Box::new(cmd_se_recover::CommandImpl), - #[cfg(feature = "with-secure-enclave")] Box::new(cmd_se_ecsign::CommandImpl), - #[cfg(feature = "with-secure-enclave")] Box::new(cmd_se_ecdh::CommandImpl), Box::new(cmd_ecverify::CommandImpl), Box::new(cmd_parseecdsasignature::CommandImpl), @@ -159,8 +148,6 @@ fn inner_main() -> CommandError { let mut features: Vec<&str> = vec![]; #[cfg(feature = "with-sequoia-openpgp")] features.push("sequoia-openpgp"); - #[cfg(feature = "with-secure-enclave")] - features.push("secure-enclave"); features }; let about = format!( diff --git a/src/seutil.rs b/src/seutil.rs index 2a0478e..7eeab38 100644 --- a/src/seutil.rs +++ b/src/seutil.rs @@ -1,113 +1,63 @@ -use crate::util::{base64_decode, base64_encode}; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; use rust_util::XResult; -use swift_rs::swift; -use swift_rs::{Bool, SRString}; - -swift!(fn is_support_secure_enclave() -> Bool); -swift!(fn generate_secure_enclave_p256_ecdh_keypair() -> SRString); -swift!(fn generate_secure_enclave_p256_ecsign_keypair() -> SRString); -swift!(fn compute_secure_enclave_p256_ecdh(private_key_base64: SRString, ephemera_public_key_base64: SRString) -> SRString); -swift!(fn compute_secure_enclave_p256_ecsign(private_key_base64: SRString, content: SRString) -> SRString); -swift!(fn recover_secure_enclave_p256_ecsign_public_key(private_key_base64: SRString) -> SRString); -swift!(fn recover_secure_enclave_p256_ecdh_public_key(private_key_base64: SRString) -> SRString); +use swift_secure_enclave_tool_rs::KeyPurpose; pub fn is_support_se() -> bool { - unsafe { is_support_secure_enclave() } + swift_secure_enclave_tool_rs::is_secure_enclave_supported().unwrap_or(false) } pub fn generate_secure_enclave_p256_keypair(sign: bool) -> XResult<(Vec, Vec, String)> { - let p256_keypair_result = if sign { - unsafe { generate_secure_enclave_p256_ecsign_keypair() } + let key_material = if sign { + swift_secure_enclave_tool_rs::generate_ecdsa_keypair(KeyPurpose::Signing, true)? } else { - unsafe { generate_secure_enclave_p256_ecdh_keypair() } + swift_secure_enclave_tool_rs::generate_ecdsa_keypair(KeyPurpose::KeyAgreement, true)? }; - parse_p256_keypair_result(p256_keypair_result.as_str()) + Ok(( + key_material.public_key_point, + key_material.public_key_der, + STANDARD.encode(&key_material.private_key_representation), + )) } pub fn recover_secure_enclave_p256_public_key( private_key: &str, sign: bool, ) -> XResult<(Vec, Vec, String)> { - let p256_keypair_result = if sign { - unsafe { recover_secure_enclave_p256_ecsign_public_key(SRString::from(private_key)) } + let private_key_representation = STANDARD.decode(private_key)?; + let key_material = if sign { + swift_secure_enclave_tool_rs::recover_ecdsa_keypair( + KeyPurpose::Signing, + &private_key_representation, + ) } else { - unsafe { recover_secure_enclave_p256_ecdh_public_key(SRString::from(private_key)) } - }; - parse_p256_keypair_result(p256_keypair_result.as_str()) + swift_secure_enclave_tool_rs::recover_ecdsa_keypair( + KeyPurpose::KeyAgreement, + &private_key_representation, + ) + }?; + Ok(( + key_material.public_key_point, + key_material.public_key_der, + STANDARD.encode(&key_material.private_key_representation), + )) } pub fn secure_enclave_p256_dh( private_key: &str, ephemeral_public_key_bytes: &[u8], ) -> XResult> { - let dh_result = unsafe { - compute_secure_enclave_p256_ecdh( - SRString::from(private_key), - SRString::from(base64_encode(ephemeral_public_key_bytes).as_str()), - ) - }; - let dh_result_str = dh_result.as_str(); - debugging!("DH result: {}", &dh_result_str); - if !dh_result_str.starts_with("ok:SharedSecret:") { - return simple_error!("ECDH P256 in secure enclave failed: {}", dh_result_str); - } - - let shared_secret_hex = dh_result_str - .chars() - .skip("ok:SharedSecret:".len()) - .collect::(); - let shared_secret_hex = shared_secret_hex.trim(); - - Ok(opt_result!( - hex::decode(shared_secret_hex), - "Decrypt shared secret hex: {}, failed: {}", - shared_secret_hex - )) + let private_key_representation = STANDARD.decode(private_key)?; + let shared_secret = swift_secure_enclave_tool_rs::private_key_ecdh( + &private_key_representation, + ephemeral_public_key_bytes, + )?; + Ok(shared_secret) } pub fn secure_enclave_p256_sign(private_key: &str, content: &[u8]) -> XResult> { - let signature_result = unsafe { - compute_secure_enclave_p256_ecsign( - SRString::from(private_key), - SRString::from(base64_encode(content).as_str()), - ) - }; - let signature_result_str = signature_result.as_str(); - debugging!("Signature result: {}", &signature_result_str); - if !signature_result_str.starts_with("ok:") { - return simple_error!( - "Sign P256 in secure enclave failed: {}", - signature_result_str - ); - } - let signature = signature_result_str.chars().skip(3).collect::(); - debugging!("Signature: {}", &signature); - Ok(base64_decode(&signature)?) -} - -fn parse_p256_keypair_result(p256_keypair_result_str: &str) -> XResult<(Vec, Vec, String)> { - if !p256_keypair_result_str.starts_with("ok:") { - return simple_error!( - "Generate P256 in secure enclave failed: {}", - p256_keypair_result_str - ); - } - let public_key_and_private_key = p256_keypair_result_str.chars().skip(3).collect::(); - let public_key_and_private_keys = public_key_and_private_key.split(',').collect::>(); - if public_key_and_private_keys.len() != 3 { - return simple_error!( - "Generate P256 in secure enclave result is bad: {}", - public_key_and_private_key - ); - } - let public_key_point = opt_result!( - base64_decode(public_key_and_private_keys[0]), - "Public key point is not base64 encoded: {}" - ); - let public_key_der = opt_result!( - base64_decode(public_key_and_private_keys[1]), - "Public key der is not base64 encoded: {}" - ); - let private_key = public_key_and_private_keys[2].to_string(); - Ok((public_key_point, public_key_der, private_key)) + let private_key_representation = STANDARD.decode(private_key)?; + let signature = + swift_secure_enclave_tool_rs::private_key_ecdsa_sign(&private_key_representation, content)?; + Ok(signature) } diff --git a/swift-lib/Package.swift b/swift-lib/Package.swift deleted file mode 100644 index 997dad6..0000000 --- a/swift-lib/Package.swift +++ /dev/null @@ -1,30 +0,0 @@ -// swift-tools-version:5.3 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "swift-lib", - platforms: [ - .macOS(.v11), // macOS Catalina. Earliest version that is officially supported by Apple. - ], - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "swift-lib", - type: .static, - targets: ["swift-lib"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - .package(name: "SwiftRs", path: "../swift-rs") - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "swift-lib", - dependencies: [.product(name: "SwiftRs", package: "SwiftRs")], - path: "src") - ] -) \ No newline at end of file diff --git a/swift-lib/src/lib.swift b/swift-lib/src/lib.swift deleted file mode 100644 index 83f8d10..0000000 --- a/swift-lib/src/lib.swift +++ /dev/null @@ -1,154 +0,0 @@ -import SwiftRs -import CryptoKit -import LocalAuthentication - -// reference: -// https://zenn.dev/iceman/scraps/380f69137c7ea2 -// https://www.andyibanez.com/posts/cryptokit-secure-enclave/ -@_cdecl("is_support_secure_enclave") -func isSupportSecureEnclave() -> Bool { - return SecureEnclave.isAvailable -} - -@_cdecl("generate_secure_enclave_p256_ecdh_keypair") -func generateSecureEnclaveP256KeyPairEcdh() -> SRString { - return generateSecureEnclaveP256KeyPair(sign: false); -} - -@_cdecl("generate_secure_enclave_p256_ecsign_keypair") -func generateSecureEnclaveP256KeyPairEcsign() -> SRString { - return generateSecureEnclaveP256KeyPair(sign: true); -} - -func generateSecureEnclaveP256KeyPair(sign: Bool) -> SRString { - var error: Unmanaged? = nil; - guard let accessCtrl = SecAccessControlCreateWithFlags( - nil, - kSecAttrAccessibleWhenUnlockedThisDeviceOnly, - [.privateKeyUsage, .biometryCurrentSet], - &error - ) else { - return SRString("err:\(error.debugDescription)") - } - do { - if (sign) { - let privateKeyReference = try SecureEnclave.P256.Signing.PrivateKey.init( - accessControl: accessCtrl - ); - let publicKeyBase64 = privateKeyReference.publicKey.x963Representation.base64EncodedString() - let publicKeyPem = privateKeyReference.publicKey.derRepresentation.base64EncodedString() - let dataRepresentationBase64 = privateKeyReference.dataRepresentation.base64EncodedString() - return SRString("ok:\(publicKeyBase64),\(publicKeyPem),\(dataRepresentationBase64)") - } else { - let privateKeyReference = try SecureEnclave.P256.KeyAgreement.PrivateKey.init( - accessControl: accessCtrl - ); - let publicKeyBase64 = privateKeyReference.publicKey.x963Representation.base64EncodedString() - let publicKeyPem = privateKeyReference.publicKey.derRepresentation.base64EncodedString() - let dataRepresentationBase64 = privateKeyReference.dataRepresentation.base64EncodedString() - return SRString("ok:\(publicKeyBase64),\(publicKeyPem),\(dataRepresentationBase64)") - } - } catch { - return SRString("err:\(error)") - } -} - -@_cdecl("recover_secure_enclave_p256_ecsign_public_key") -func recoverSecureEnclaveP256PublicKeyEcsign(privateKeyDataRepresentation: SRString) -> SRString { - return recoverSecureEnclaveP256PublicKey(privateKeyDataRepresentation: privateKeyDataRepresentation, sign: true); -} - -@_cdecl("recover_secure_enclave_p256_ecdh_public_key") -func recoverSecureEnclaveP256PublicKeyEcdh(privateKeyDataRepresentation: SRString) -> SRString { - return recoverSecureEnclaveP256PublicKey(privateKeyDataRepresentation: privateKeyDataRepresentation, sign: false); -} - -func recoverSecureEnclaveP256PublicKey(privateKeyDataRepresentation: SRString, sign: Bool) -> SRString { - guard let privateKeyDataRepresentation = Data( - base64Encoded: privateKeyDataRepresentation.toString() - ) else { - return SRString("err:private key base64 decode failed") - } - do { - let context = LAContext(); - if (sign) { - let privateKeyReference = try SecureEnclave.P256.Signing.PrivateKey( - dataRepresentation: privateKeyDataRepresentation, - authenticationContext: context - ) - let publicKeyBase64 = privateKeyReference.publicKey.x963Representation.base64EncodedString() - let publicKeyPem = privateKeyReference.publicKey.derRepresentation.base64EncodedString() - let dataRepresentationBase64 = privateKeyReference.dataRepresentation.base64EncodedString() - return SRString("ok:\(publicKeyBase64),\(publicKeyPem),\(dataRepresentationBase64)") - } else { - let privateKeyReference = try SecureEnclave.P256.KeyAgreement.PrivateKey( - dataRepresentation: privateKeyDataRepresentation, - authenticationContext: context - ) - let publicKeyBase64 = privateKeyReference.publicKey.x963Representation.base64EncodedString() - let publicKeyPem = privateKeyReference.publicKey.derRepresentation.base64EncodedString() - let dataRepresentationBase64 = privateKeyReference.dataRepresentation.base64EncodedString() - return SRString("ok:\(publicKeyBase64),\(publicKeyPem),\(dataRepresentationBase64)") - } - } catch { - return SRString("err:\(error)") - } -} - -@_cdecl("compute_secure_enclave_p256_ecdh") -func computeSecureEnclaveP256Ecdh(privateKeyDataRepresentation: SRString, ephemeraPublicKey: SRString) -> SRString { - guard let privateKeyDataRepresentation = Data( - base64Encoded: privateKeyDataRepresentation.toString() - ) else { - return SRString("err:private key base64 decode failed") - } - guard let ephemeralPublicKeyRepresentation = Data( - base64Encoded: ephemeraPublicKey.toString() - ) else { - return SRString("err:ephemeral public key base64 decode failed") - } - do { - let context = LAContext(); - let p = try SecureEnclave.P256.KeyAgreement.PrivateKey( - dataRepresentation: privateKeyDataRepresentation, - authenticationContext: context - ) - - let ephemeralPublicKey = try P256.KeyAgreement.PublicKey.init(derRepresentation: ephemeralPublicKeyRepresentation) - - let sharedSecret = try p.sharedSecretFromKeyAgreement( - with: ephemeralPublicKey) - - return SRString("ok:\(sharedSecret.description)") - } catch { - return SRString("err:\(error)") - } -} - -@_cdecl("compute_secure_enclave_p256_ecsign") -func computeSecureEnclaveP256Ecsign(privateKeyDataRepresentation: SRString, content: SRString) -> SRString { - guard let privateKeyDataRepresentation = Data( - base64Encoded: privateKeyDataRepresentation.toString() - ) else { - return SRString("err:private key base64 decode failed") - } - guard let contentData = Data( - base64Encoded: content.toString() - ) else { - return SRString("err:content base64 decode failed") - } - do { - let context = LAContext(); - let p = try SecureEnclave.P256.Signing.PrivateKey( - dataRepresentation: privateKeyDataRepresentation, - authenticationContext: context - ) - - let digest = SHA256.hash(data: contentData) - let signature = try p.signature(for: digest) - - return SRString("ok:\(signature.derRepresentation.base64EncodedString())") - } catch { - return SRString("err:\(error)") - } -} \ No newline at end of file diff --git a/swift-rs/Cargo.lock b/swift-rs/Cargo.lock deleted file mode 100644 index bd75097..0000000 --- a/swift-rs/Cargo.lock +++ /dev/null @@ -1,417 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.164" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "proc-macro2" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.215" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "serde_json" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serial_test" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c789ec87f4687d022a2405cf46e0cd6284889f1839de292cadeb6c6019506f2" -dependencies = [ - "dashmap", - "futures", - "lazy_static", - "log", - "parking_lot", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "swift-rs-hatter-fork" -version = "1.0.6" -dependencies = [ - "base64", - "serde", - "serde_json", - "serial_test", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/swift-rs/Cargo.toml b/swift-rs/Cargo.toml deleted file mode 100644 index 330d9a9..0000000 --- a/swift-rs/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "swift-rs" -version = "1.0.7" -description = "Call Swift from Rust with ease!" -authors = ["The swift-rs contributors"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/Brendonovich/swift-rs" -edition = "2021" -exclude=["/src-swift", "*.swift"] -build = "src-rs/test-build.rs" - -# /bin/sh RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features -[package.metadata."docs.rs"] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[lib] -path = "src-rs/lib.rs" - -[dependencies] -base64 = "0.22" -serde = { version = "1.0", features = ["derive"], optional = true} -serde_json = { version = "1.0", optional = true } - -[build-dependencies] -serde = { version = "1.0", features = ["derive"]} -serde_json = { version = "1.0" } - -[dev-dependencies] -serial_test = "0.10" - -[features] -default = [] -build = ["serde", "serde_json"] diff --git a/swift-rs/LICENSE-APACHE b/swift-rs/LICENSE-APACHE deleted file mode 100644 index 6e7334b..0000000 --- a/swift-rs/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2023 The swift-rs developers - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/swift-rs/LICENSE-MIT b/swift-rs/LICENSE-MIT deleted file mode 100644 index dd89749..0000000 --- a/swift-rs/LICENSE-MIT +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2023 The swift-rs Developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/swift-rs/Package.swift b/swift-rs/Package.swift deleted file mode 100644 index eb1c07d..0000000 --- a/swift-rs/Package.swift +++ /dev/null @@ -1,30 +0,0 @@ -// swift-tools-version:5.3 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "SwiftRs", - platforms: [ - .macOS(.v10_13), - .iOS(.v11), - ], - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "SwiftRs", - targets: ["SwiftRs"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "SwiftRs", - dependencies: [], - path: "src-swift") - ] -) diff --git a/swift-rs/README.md b/swift-rs/README.md deleted file mode 100644 index 2e83a2d..0000000 --- a/swift-rs/README.md +++ /dev/null @@ -1,483 +0,0 @@ -# swift-rs - -![Crates.io](https://img.shields.io/crates/v/swift-rs?color=blue&style=flat-square) -![docs.rs](https://img.shields.io/docsrs/swift-rs?color=blue&style=flat-square) - -Call Swift functions from Rust with ease! - -## Setup - -Add `swift-rs` to your project's `dependencies` and `build-dependencies`: - -```toml -[dependencies] -swift-rs = "1.0.5" - -[build-dependencies] -swift-rs = { version = "1.0.5", features = ["build"] } -``` - -Next, some setup work must be done: - -1. Ensure your swift code is organized into a Swift Package. -This can be done in XCode by selecting File -> New -> Project -> Multiplatform -> Swift Package and importing your existing code. -2. Add `SwiftRs` as a dependency to your Swift package and make the build type `.static`. -```swift -let package = Package( - dependencies: [ - .package(url: "https://github.com/Brendonovich/swift-rs", from: "1.0.5") - ], - products: [ - .library( - type: .static, - ), - ], - targets: [ - .target( - // Must specify swift-rs as a dependency of your target - dependencies: [ - .product( - name: "SwiftRs", - package: "swift-rs" - ) - ], - ) - ] -) -``` -3. Create a `build.rs` file in your project's root folder, if you don't have one already. -4. Use `SwiftLinker` in your `build.rs` file to link both the Swift runtime and your Swift package. -The package name should be the same as is specified in your `Package.swift` file, -and the path should point to your Swift project's root folder relative to your crate's root folder. - -```rust -use swift_rs::SwiftLinker; - -fn build() { - // swift-rs has a minimum of macOS 10.13 - // Ensure the same minimum supported macOS version is specified as in your `Package.swift` file. - SwiftLinker::new("10.13") - // Only if you are also targetting iOS - // Ensure the same minimum supported iOS version is specified as in your `Package.swift` file - .with_ios("11") - .with_package(PACKAGE_NAME, PACKAGE_PATH) - .link(); - - // Other build steps -} -``` - -With those steps completed, you should be ready to start using Swift code from Rust! - -If you experience the error `dyld[16008]: Library not loaded: @rpath/libswiftCore.dylib` -when using `swift-rs` with [Tauri](https://tauri.app) ensure you have set your -[Tauri minimum system version](https://tauri.app/v1/guides/building/macos#setting-a-minimum-system-version) -to `10.15` or higher in your `tauri.config.json`. - -## Calling basic functions - -To allow calling a Swift function from Rust, it must follow some rules: - -1. It must be global -2. It must be annotated with `@_cdecl`, so that it is callable from C -3. It must only use types that can be represented in Objective-C, -so only classes that derive `NSObject`, as well as scalars such as Int and Bool. -This excludes strings, arrays, generics (though all of these can be sent with workarounds) -and structs (which are strictly forbidden). - -For this example we will use a function that simply squares a number: - -```swift -public func squareNumber(number: Int) -> Int { - return number * number -} -``` - -So far, this function meets requirements 1 and 3: it is global and public, and only uses the Int type, which is Objective-C compatible. -However, it is not annotated with `@_cdecl`. -To fix this, we must call `@_cdecl` before the function's declaration and specify the name that the function is exposed to Rust with as its only argument. -To keep with Rust's naming conventions, we will export this function in snake case as `square_number`. - -```swift -@_cdecl("square_number") -public func squareNumber(number: Int) -> Int { - return number * number -} -``` - -Now that `squareNumber` is properly exposed to Rust, we can start interfacing with it. -This can be done using the `swift!` macro, with the `Int` type helping to provide a similar function signature: - -```rust -use swift_rs::swift; - -swift!(fn square_number(number: Int) -> Int); -``` - -Lastly, you can call the function from regular Rust functions. -Note that all calls to a Swift function are unsafe, -and require wrapping in an `unsafe {}` block or `unsafe fn`. - -```rust -fn main() { - let input: Int = 4; - let output = unsafe { square_number(input) }; - - println!("Input: {}, Squared: {}", input, output); - // Prints "Input: 4, Squared: 16" -} -``` - -Check [the documentation](TODO) for all available helper types. - -## Returning objects from Swift - -Let's say that we want our `squareNumber` function to return not only the result, but also the original input. -A standard way to do this in Swift would be with a struct: - -```swift -struct SquareNumberResult { - var input: Int - var output: Int -} -``` - -We are not allowed to do this, though, since structs cannot be represented in Objective-C. -Instead, we must use a class that extends `NSObject`: - -```swift -class SquareNumberResult: NSObject { - var input: Int - var output: Int - - init(_ input: Int, _ output: Int) { - self.input = input; - self.output = output - } -} -``` - -Yes, this class could contain the squaring logic too, but that is irrelevant for this example - -An instance of this class can then be returned from `squareNumber`: - -```swift -@_cdecl("square_number") -public func squareNumber(input: Int) -> SquareNumberResult { - let output = input * input - return SquareNumberResult(input, output) -} -``` - -As you can see, returning an `NSObject` from Swift isn't too difficult. -The same can't be said for the Rust implementation, though. -`squareNumber` doesn't actually return a struct containing `input` and `output`, -but instead a pointer to a `SquareNumberResult` stored somewhere in memory. -Additionally, this value contains more data than just `input` and `output`: -Since it is an `NSObject`, it contains extra data that must be accounted for when using it in Rust. - -This may sound daunting, but it's not actually a problem thanks to `SRObject`. -This type manages the pointer internally, and takes a generic argument for a struct that we can access the data through. -Let's see how we'd implement `SquareNumberResult` in Rust: - -```rust -use swift_rs::{swift, Int, SRObject}; - -// Any struct that is used in a C function must be annotated -// with this, and since our Swift function is exposed as a -// C function with @_cdecl, this is necessary here -#[repr(C)] -// Struct matches the class declaration in Swift -struct SquareNumberResult { - input: Int, - output: Int -} - -// SRObject abstracts away the underlying pointer and will automatically deref to -// &SquareNumberResult through the Deref trait -swift!(fn square_number(input: Int) -> SRObject); -``` - -Then, using the new return value is just like using `SquareNumberResult` directly: - -```rust -fn main() { - let input = 4; - let result = unsafe { square_number(input) }; - - let result_input = result.input; // 4 - let result_output = result.output; // 16 -} -``` - -Creating objects in Rust and then passing them to Swift is not supported. - -## Optionals - -`swift-rs` also supports Swift's `nil` type, but only for functions that return optional `NSObject`s. -Functions returning optional primitives cannot be represented in Objective C, and thus are not supported. - -Let's say we have a function returning an optional `SRString`: - -```swift -@_cdecl("optional_string") -func optionalString(returnNil: Bool) -> SRString? { - if (returnNil) return nil - else return SRString("lorem ipsum") -} -``` - -Thanks to Rust's [null pointer optimisation](https://doc.rust-lang.org/std/option/index.html#representation), -the optional nature of `SRString?` can be represented by wrapping `SRString` in Rust's `Option` type! - -```rust -use swift_rs::{swift, Bool, SRString}; - -swift!(optional_string(return_nil: Bool) -> Option) -``` - -Null pointers are actually the reason why a function that returns an optional primitive cannot be represented in C. -If this were to be supported, how could a `nil` be differentiated from a number? It can't! - -## Complex types - -So far we have only looked at using primitive types and structs/classes, -but this leaves out some of the most important data structures: arrays (`SRArray`) and strings (`SRString`). -These types must be treated with caution, however, and are not as flexible as their native Swift & Rust counterparts. - -### Strings - -Strings can be passed between Rust and Swift through `SRString`, which can be created from native strings in either language. - -**As an argument** - -```swift -import SwiftRs - -@_cdecl("swift_print") -public func swiftPrint(value: SRString) { - // .to_string() converts the SRString to a Swift String - print(value.to_string()) -} -``` - -```rust -use swift_rs::{swift, SRString, SwiftRef}; - -swift!(fn swift_print(value: &SRString)); - -fn main() { - // SRString can be created by simply calling .into() on any string reference. - // This will allocate memory in Swift and copy the string - let value: SRString = "lorem ipsum".into(); - - unsafe { swift_print(&value) }; // Will print "lorem ipsum" to the console -} -``` - -**As a return value** - -```swift -import SwiftRs - -@_cdecl("get_string") -public func getString() -> SRString { - let value = "lorem ipsum" - - // SRString can be created from a regular String - return SRString(value) -} -``` - -```rust -use swift_rs::{swift, SRString}; - -swift!(fn get_string() -> SRString); - -fn main() { - let value_srstring = unsafe { get_string() }; - - // SRString can be converted to an &str using as_str()... - let value_str: &str = value_srstring.as_str(); - // or though the Deref trait - let value_str: &str = &*value_srstring; - - // SRString also implements Display - println!("{}", value_srstring); // Will print "lorem ipsum" to the console -} -``` - -### Arrays - -**Primitive Arrays** - -Representing arrays properly is tricky, since we cannot use generics as Swift arguments or return values according to rule 3. -Instead, `swift-rs` provides a generic `SRArray` that can be embedded inside another class that extends `NSObject` that is not generic, -but is restricted to a single element type. - -```swift -import SwiftRs - -// Argument/Return values can contain generic types, but cannot be generic themselves. -// This includes extending generic types. -class IntArray: NSObject { - var data: SRArray - - init(_ data: [Int]) { - self.data = SRArray(data) - } -} - -@_cdecl("get_numbers") -public func getNumbers() -> IntArray { - let numbers = [1, 2, 3, 4] - - return IntArray(numbers) -} -``` - -```rust -use swift_rs::{Int, SRArray, SRObject}; - -#[repr(C)] -struct IntArray { - data: SRArray -} - -// Since IntArray extends NSObject in its Swift implementation, -// it must be wrapped in SRObject on the Rust side -swift!(fn get_numbers() -> SRObject); - -fn main() { - let numbers = unsafe { get_numbers() }; - - // SRArray can be accessed as a slice via as_slice - let numbers_slice: &[Int] = numbers.data.as_slice(); - - assert_eq!(numbers_slice, &[1, 2, 3, 4]); -} -``` - -To simplify things on the rust side, we can actually do away with the `IntArray` struct. -Since `IntArray` only has one field, its memory layout is identical to that of `SRArray`, -so our Rust implementation can be simplified at the cost of equivalence with our Swift code: - -```rust -// We still need to wrap the array in SRObject since -// the wrapper class in Swift is an NSObject -swift!(fn get_numbers() -> SRObject>); -``` - -**NSObject Arrays** - -What if we want to return an `NSObject` array? There are two options on the Swift side: - -1. Continue using `SRArray` and a custom wrapper type, or -2. Use `SRObjectArray`, a wrapper type provided by `swift-rs` that accepts any `NSObject` as its elements. -This can be easier than continuing to create wrapper types, but sacrifices some type safety. - -There is also `SRObjectArray` for Rust, which is compatible with any single-element Swift wrapper type (and of course `SRObjectArray` in Swift), -and automatically wraps its elements in `SRObject`, so there's very little reason to not use it unless you _really_ like custom wrapper types. - -Using `SRObjectArray` in both Swift and Rust with a basic custom class/struct can be done like this: - -```swift -import SwiftRs - -class IntTuple: NSObject { - var item1: Int - var item2: Int - - init(_ item1: Int, _ item2: Int) { - self.item1 = item1 - self.item2 = item2 - } -} - -@_cdecl("get_tuples") -public func getTuples() -> SRObjectArray { - let tuple1 = IntTuple(0,1), - tuple2 = IntTuple(2,3), - tuple3 = IntTuple(4,5) - - let tupleArray: [IntTuple] = [ - tuple1, - tuple2, - tuple3 - ] - - // Type safety is only lost when the Swift array is converted to an SRObjectArray - return SRObjectArray(tupleArray) -} -``` - -```rust -use swift_rs::{swift, Int, SRObjectArray}; - -#[repr(C)] -struct IntTuple { - item1: Int, - item2: Int -} - -// No need to wrap IntTuple in SRObject since -// SRObjectArray does it automatically -swift!(fn get_tuples() -> SRObjectArray); - -fn main() { - let tuples = unsafe { get_tuples() }; - - for tuple in tuples.as_slice() { - // Will print each tuple's contents to the console - println!("Item 1: {}, Item 2: {}", tuple.item1, tuple.item2); - } -} -``` - -Complex types can contain whatever combination of primitives and `SRObject` you like, just remember to follow the 3 rules! - -## Bonuses - -### SRData - -A wrapper type for `SRArray` designed for storing `u8`s - essentially just a byte buffer. - -### Tighter Memory Control with `autoreleasepool!` - -If you've come to Swift from an Objective-C background, you likely know the utility of `@autoreleasepool` blocks. -`swift-rs` has your back on this too, just wrap your block of code with a `autoreleasepool!`, and that block of code now executes with its own autorelease pool! - -```rust -use swift_rs::autoreleasepool; - -for _ in 0..10000 { - autoreleasepool!({ - // do some memory intensive thing here - }); -} -``` - -## Limitations - -Currently, the only types that can be created from Rust are number types, boolean, `SRString`, and `SRData`. -This is because those types are easy to allocate memory for, either on the stack or on the heap via calling out to swift, -whereas other types are not. This may be implemented in the future, though. - -Mutating values across Swift and Rust is not currently an aim for this library, it is purely for providing arguments and returning values. -Besides, this would go against Rust's programming model, potentially allowing for multiple shared references to a value instead of interior mutability via something like a Mutex. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/swift-rs/src-rs/autorelease.rs b/swift-rs/src-rs/autorelease.rs deleted file mode 100644 index bb1007d..0000000 --- a/swift-rs/src-rs/autorelease.rs +++ /dev/null @@ -1,26 +0,0 @@ -/// Run code with its own autorelease pool. Semantically, this is identical -/// to [`@autoreleasepool`](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html) -/// in Objective-C -/// -/// -/// ```no_run -/// use swift_rs::autoreleasepool; -/// -/// autoreleasepool!({ -/// // do something memory intensive stuff -/// }) -/// ``` -#[macro_export] -macro_rules! autoreleasepool { - ( $expr:expr ) => {{ - extern "C" { - fn objc_autoreleasePoolPush() -> *mut std::ffi::c_void; - fn objc_autoreleasePoolPop(context: *mut std::ffi::c_void); - } - - let pool = unsafe { objc_autoreleasePoolPush() }; - let r = { $expr }; - unsafe { objc_autoreleasePoolPop(pool) }; - r - }}; -} diff --git a/swift-rs/src-rs/build.rs b/swift-rs/src-rs/build.rs deleted file mode 100644 index 394b9eb..0000000 --- a/swift-rs/src-rs/build.rs +++ /dev/null @@ -1,326 +0,0 @@ -#![allow(dead_code)] -use std::{env, fmt::Display, path::Path, path::PathBuf, process::Command}; - -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SwiftTarget { - triple: String, - unversioned_triple: String, - module_triple: String, - //pub swift_runtime_compatibility_version: String, - #[serde(rename = "librariesRequireRPath")] - libraries_require_rpath: bool, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SwiftPaths { - runtime_library_paths: Vec, - runtime_library_import_paths: Vec, - runtime_resource_path: String, -} - -#[derive(Deserialize)] -struct SwiftEnv { - target: SwiftTarget, - paths: SwiftPaths, -} - -impl SwiftEnv { - fn new(minimum_macos_version: &str, minimum_ios_version: Option<&str>) -> Self { - let rust_target = RustTarget::from_env(); - let target = rust_target.swift_target_triple(minimum_macos_version, minimum_ios_version); - - let swift_target_info_str = Command::new("swift") - .args(["-target", &target, "-print-target-info"]) - .output() - .unwrap() - .stdout; - - serde_json::from_slice(&swift_target_info_str).unwrap() - } -} - -#[allow(clippy::upper_case_acronyms)] -enum RustTargetOS { - MacOS, - IOS, -} - -impl RustTargetOS { - fn from_env() -> Self { - match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { - "macos" => RustTargetOS::MacOS, - "ios" => RustTargetOS::IOS, - _ => panic!("unexpected target operating system"), - } - } - - fn to_swift(&self) -> &'static str { - match self { - Self::MacOS => "macosx", - Self::IOS => "ios", - } - } -} - -impl Display for RustTargetOS { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MacOS => write!(f, "macos"), - Self::IOS => write!(f, "ios"), - } - } -} - -#[allow(clippy::upper_case_acronyms)] -enum SwiftSDK { - MacOS, - IOS, - IOSSimulator, -} - -impl SwiftSDK { - fn from_os(os: &RustTargetOS) -> Self { - let target = env::var("TARGET").unwrap(); - let simulator = target.ends_with("ios-sim") - || (target.starts_with("x86_64") && target.ends_with("ios")); - - match os { - RustTargetOS::MacOS => Self::MacOS, - RustTargetOS::IOS if simulator => Self::IOSSimulator, - RustTargetOS::IOS => Self::IOS, - } - } - - fn clang_lib_extension(&self) -> &'static str { - match self { - Self::MacOS => "osx", - Self::IOS => "ios", - Self::IOSSimulator => "iossim", - } - } -} - -impl Display for SwiftSDK { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MacOS => write!(f, "macosx"), - Self::IOSSimulator => write!(f, "iphonesimulator"), - Self::IOS => write!(f, "iphoneos"), - } - } -} - -struct RustTarget { - arch: String, - os: RustTargetOS, - sdk: SwiftSDK, -} - -impl RustTarget { - fn from_env() -> Self { - let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - let os = RustTargetOS::from_env(); - let sdk = SwiftSDK::from_os(&os); - - Self { arch, os, sdk } - } - - fn swift_target_triple( - &self, - minimum_macos_version: &str, - minimum_ios_version: Option<&str>, - ) -> String { - let unversioned = self.unversioned_swift_target_triple(); - format!( - "{unversioned}{}{}", - match (&self.os, minimum_ios_version) { - (RustTargetOS::MacOS, _) => minimum_macos_version, - (RustTargetOS::IOS, Some(version)) => version, - _ => "", - }, - // simulator suffix - matches!(self.sdk, SwiftSDK::IOSSimulator) - .then(|| "-simulator".to_string()) - .unwrap_or_default() - ) - } - - fn unversioned_swift_target_triple(&self) -> String { - format!( - "{}-apple-{}", - match self.arch.as_str() { - "aarch64" => "arm64", - a => a, - }, - self.os.to_swift(), - ) - } -} - -struct SwiftPackage { - name: String, - path: PathBuf, -} - -/// Builder for linking the Swift runtime and custom packages. -#[cfg(feature = "build")] -pub struct SwiftLinker { - packages: Vec, - macos_min_version: String, - ios_min_version: Option, -} - -impl SwiftLinker { - /// Creates a new [`SwiftLinker`] with a minimum macOS verison. - /// - /// Minimum macOS version must be at least 10.13. - pub fn new(macos_min_version: &str) -> Self { - Self { - packages: vec![], - macos_min_version: macos_min_version.to_string(), - ios_min_version: None, - } - } - - /// Instructs the [`SwiftLinker`] to also compile for iOS - /// using the specified minimum iOS version. - /// - /// Minimum iOS version must be at least 11. - pub fn with_ios(mut self, min_version: &str) -> Self { - self.ios_min_version = Some(min_version.to_string()); - self - } - - /// Adds a package to be linked against. - /// `name` should match the `name` field in your `Package.swift`, - /// and `path` should point to the root of your Swift package relative - /// to your crate's root. - pub fn with_package(mut self, name: &str, path: impl AsRef) -> Self { - self.packages.extend([SwiftPackage { - name: name.to_string(), - path: path.as_ref().into(), - }]); - - self - } - - /// Links the Swift runtime, then builds and links the provided packages. - /// This does not (yet) automatically rebuild your Swift files when they are modified, - /// you'll need to modify/save your `build.rs` file for that. - pub fn link(self) { - let swift_env = SwiftEnv::new(&self.macos_min_version, self.ios_min_version.as_deref()); - - #[allow(clippy::uninlined_format_args)] - for path in swift_env.paths.runtime_library_paths { - println!("cargo:rustc-link-search=native={path}"); - } - - let debug = env::var("DEBUG").unwrap() == "true"; - let configuration = if debug { "debug" } else { "release" }; - let rust_target = RustTarget::from_env(); - - link_clang_rt(&rust_target); - - for package in self.packages { - let package_path = - Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join(&package.path); - let out_path = Path::new(&env::var("OUT_DIR").unwrap()) - .join("swift-rs") - .join(&package.name); - - let sdk_path_output = Command::new("xcrun") - .args(["--sdk", &rust_target.sdk.to_string(), "--show-sdk-path"]) - .output() - .unwrap(); - if !sdk_path_output.status.success() { - panic!( - "Failed to get SDK path with `xcrun --sdk {} --show-sdk-path`", - rust_target.sdk - ); - } - - let sdk_path = String::from_utf8_lossy(&sdk_path_output.stdout); - - let mut command = Command::new("swift"); - command.current_dir(&package.path); - - let arch = match std::env::consts::ARCH { - "aarch64" => "arm64", - arch => arch, - }; - - command - // Build the package (duh) - .args(["build"]) - // SDK path for regular compilation (idk) - .args(["--sdk", sdk_path.trim()]) - // Release/Debug configuration - .args(["-c", configuration]) - .args(["--arch", arch]) - // Where the artifacts will be generated to - .args(["--build-path", &out_path.display().to_string()]) - // Override SDK path for each swiftc instance. - // Necessary for iOS compilation. - .args(["-Xswiftc", "-sdk"]) - .args(["-Xswiftc", sdk_path.trim()]) - // Override target triple for each swiftc instance. - // Necessary for iOS compilation. - .args(["-Xswiftc", "-target"]) - .args([ - "-Xswiftc", - &rust_target.swift_target_triple( - &self.macos_min_version, - self.ios_min_version.as_deref(), - ), - ]); - - if !command.status().unwrap().success() { - panic!("Failed to compile swift package {}", package.name); - } - - let search_path = out_path - // swift build uses this output folder no matter what is the target - .join(format!( - "{}-apple-macosx", - arch - )) - .join(configuration); - - println!("cargo:rerun-if-changed={}", package_path.display()); - println!("cargo:rustc-link-search=native={}", search_path.display()); - println!("cargo:rustc-link-lib=static={}", package.name); - } - } -} - -fn link_clang_rt(rust_target: &RustTarget) { - println!( - "cargo:rustc-link-lib=clang_rt.{}", - rust_target.sdk.clang_lib_extension() - ); - println!("cargo:rustc-link-search={}", clang_link_search_path()); -} - -fn clang_link_search_path() -> String { - let output = std::process::Command::new( - std::env::var("SWIFT_RS_CLANG").unwrap_or_else(|_| "/usr/bin/clang".to_string()), - ) - .arg("--print-search-dirs") - .output() - .unwrap(); - if !output.status.success() { - panic!("Can't get search paths from clang"); - } - let stdout = String::from_utf8_lossy(&output.stdout); - for line in stdout.lines() { - if line.contains("libraries: =") { - let path = line.split('=').nth(1).unwrap(); - return format!("{}/lib/darwin", path); - } - } - panic!("clang is missing search paths"); -} diff --git a/swift-rs/src-rs/dark_magic.rs b/swift-rs/src-rs/dark_magic.rs deleted file mode 100644 index 7cb8c36..0000000 --- a/swift-rs/src-rs/dark_magic.rs +++ /dev/null @@ -1,90 +0,0 @@ -/// This retain-balancing algorithm is cool but likely isn't required. -/// I'm keeping it around in case it's necessary one day. - -// #[derive(Clone, Copy, Debug)] -// enum ValueArity { -// Reference, -// Value, -// } - -// pub unsafe fn balance_ptrs(args: Vec<(*const c_void, bool)>, ret: Vec<(*const c_void, bool)>) { -// fn collect_references( -// v: Vec<(*const c_void, bool)>, -// ) -> BTreeMap<*const c_void, Vec> { -// v.into_iter().fold( -// BTreeMap::<_, Vec>::new(), -// |mut map, (ptr, is_ref)| { -// map.entry(ptr).or_default().push(if is_ref { -// ValueArity::Reference -// } else { -// ValueArity::Value -// }); -// map -// }, -// ) -// } - -// let mut args = collect_references(args); -// let mut ret = collect_references(ret); - -// let both_counts = args -// .clone() -// .into_iter() -// .flat_map(|(arg, values)| { -// ret.remove(&arg).map(|ret| { -// args.remove(&arg); - -// let ret_values = ret -// .iter() -// .filter(|v| matches!(v, ValueArity::Value)) -// .count() as isize; - -// let arg_references = values -// .iter() -// .filter(|v| matches!(v, ValueArity::Reference)) -// .count() as isize; - -// let ref_in_value_out_retains = min(ret_values, arg_references); - -// (arg, ref_in_value_out_retains) -// }) -// }) -// .collect::>(); - -// let arg_counts = args.into_iter().map(|(ptr, values)| { -// let count = values -// .into_iter() -// .filter(|v| matches!(v, ValueArity::Value)) -// .count() as isize; -// (ptr, count) -// }); - -// let ret_counts = ret -// .into_iter() -// .map(|(ptr, values)| { -// let count = values -// .into_iter() -// .filter(|v| matches!(v, ValueArity::Value)) -// .count() as isize; -// (ptr, count) -// }) -// .collect::>(); - -// both_counts -// .into_iter() -// .chain(arg_counts) -// .chain(ret_counts) -// .for_each(|(ptr, count)| match count { -// 0 => {} -// n if n > 0 => { -// for _ in 0..n { -// retain_object(ptr) -// } -// } -// n => { -// for _ in n..0 { -// release_object(ptr) -// } -// } -// }); -// } diff --git a/swift-rs/src-rs/lib.rs b/swift-rs/src-rs/lib.rs deleted file mode 100644 index 9bbf1c6..0000000 --- a/swift-rs/src-rs/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Call Swift functions from Rust with ease! -#![cfg_attr(docsrs, feature(doc_cfg))] - -mod autorelease; -mod swift; -mod swift_arg; -mod swift_ret; -mod types; - -// pub use autorelease::*; -pub use swift::*; -pub use swift_arg::*; -pub use swift_ret::*; -pub use types::*; - -#[cfg(feature = "build")] -#[cfg_attr(docsrs, doc(cfg(feature = "build")))] -mod build; -#[cfg(feature = "build")] -pub use build::*; diff --git a/swift-rs/src-rs/swift.rs b/swift-rs/src-rs/swift.rs deleted file mode 100644 index 0b4f370..0000000 --- a/swift-rs/src-rs/swift.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::ffi::c_void; - -use crate::*; - -/// Reference to an `NSObject` for internal use by [`swift!`]. -#[must_use = "A Ref MUST be sent over to the Swift side"] -#[repr(transparent)] -pub struct SwiftRef<'a, T: SwiftObject>(&'a SRObjectImpl); - -impl<'a, T: SwiftObject> SwiftRef<'a, T> { - pub(crate) unsafe fn retain(&self) { - retain_object(self.0 as *const _ as *const c_void) - } -} - -/// A type that is represented as an `NSObject` in Swift. -pub trait SwiftObject { - type Shape; - - /// Gets a reference to the `SRObject` at the root of a `SwiftObject` - fn get_object(&self) -> &SRObject; - - /// Creates a [`SwiftRef`] for an object which can be used when calling a Swift function. - /// This function should never be called manually, - /// instead you should rely on the [`swift!`] macro to call it for you. - /// - /// # Safety - /// This function converts the [`NonNull`](std::ptr::NonNull) - /// inside an [`SRObject`] into a reference, - /// implicitly assuming that the pointer is still valid. - /// The inner pointer is private, - /// and the returned [`SwiftRef`] is bound to the lifetime of the original [`SRObject`], - /// so if you use `swift-rs` as normal this function should be safe. - unsafe fn swift_ref(&self) -> SwiftRef - where - Self: Sized, - { - SwiftRef(self.get_object().0.as_ref()) - } - - /// Adds a retain to an object. - /// - /// # Safety - /// Just don't call this, let [`swift!`] handle it for you. - unsafe fn retain(&self) - where - Self: Sized, - { - self.swift_ref().retain() - } -} - -swift!(pub(crate) fn retain_object(obj: *const c_void)); -swift!(pub(crate) fn release_object(obj: *const c_void)); -swift!(pub(crate) fn data_from_bytes(data: *const u8, size: Int) -> SRData); -swift!(pub(crate) fn string_from_bytes(data: *const u8, size: Int) -> SRString); - -/// Declares a function defined in a swift library. -/// As long as this macro is used, retain counts of arguments -/// and return values will be correct. -/// -/// Use this macro as if the contents were going directly -/// into an `extern "C"` block. -/// -/// ``` -/// use swift_rs::*; -/// -/// swift!(fn echo(string: &SRString) -> SRString); -/// -/// let string: SRString = "test".into(); -/// let result = unsafe { echo(&string) }; -/// -/// assert_eq!(result.as_str(), string.as_str()) -/// ``` -/// -/// # Details -/// -/// Internally this macro creates a wrapping function around an `extern "C"` block -/// that represents the actual Swift function. This is done in order to restrict the types -/// that can be used as arguments and return types, and to ensure that retain counts of returned -/// values are appropriately balanced. -#[macro_export] -macro_rules! swift { - ($vis:vis fn $name:ident $(<$($lt:lifetime),+>)? ($($arg:ident: $arg_ty:ty),*) $(-> $ret:ty)?) => { - $vis unsafe fn $name $(<$($lt),*>)? ($($arg: $arg_ty),*) $(-> $ret)? { - extern "C" { - fn $name $(<$($lt),*>)? ($($arg: <$arg_ty as $crate::SwiftArg>::ArgType),*) $(-> $ret)?; - } - - let res = { - $(let $arg = $crate::SwiftArg::as_arg(&$arg);)* - - $name($($arg),*) - }; - - $crate::SwiftRet::retain(&res); - - res - } - }; -} diff --git a/swift-rs/src-rs/swift_arg.rs b/swift-rs/src-rs/swift_arg.rs deleted file mode 100644 index 689650e..0000000 --- a/swift-rs/src-rs/swift_arg.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::ffi::c_void; - -use crate::{swift::SwiftObject, *}; - -/// Identifies a type as being a valid argument in a Swift function. -pub trait SwiftArg<'a> { - type ArgType; - - /// Creates a swift-compatible version of the argument. - /// For primitives this just returns `self`, - /// but for [`SwiftObject`] types it wraps them in [`SwiftRef`]. - /// - /// This function is called within the [`swift!`] macro. - /// - /// # Safety - /// - /// Creating a [`SwiftRef`] is inherently unsafe, - /// but is reliable if using the [`swift!`] macro, - /// so it is not advised to call this function manually. - unsafe fn as_arg(&'a self) -> Self::ArgType; -} - -macro_rules! primitive_impl { - ($($t:ty),+) => { - $(impl<'a> SwiftArg<'a> for $t { - type ArgType = $t; - - unsafe fn as_arg(&'a self) -> Self::ArgType { - *self - } - })+ - }; -} - -primitive_impl!( - Bool, - Int, - Int8, - Int16, - Int32, - Int64, - UInt, - UInt8, - UInt16, - UInt32, - UInt64, - Float32, - Float64, - *const c_void, - *mut c_void, - *const u8, - () -); - -macro_rules! ref_impl { - ($($t:ident $(<$($gen:ident),+>)?),+) => { - $(impl<'a $($(, $gen: 'a),+)?> SwiftArg<'a> for $t$(<$($gen),+>)? { - type ArgType = SwiftRef<'a, $t$(<$($gen),+>)?>; - - unsafe fn as_arg(&'a self) -> Self::ArgType { - self.swift_ref() - } - })+ - }; -} - -ref_impl!(SRObject, SRArray, SRData, SRString); - -impl<'a, T: SwiftArg<'a>> SwiftArg<'a> for &T { - type ArgType = T::ArgType; - - unsafe fn as_arg(&'a self) -> Self::ArgType { - (*self).as_arg() - } -} diff --git a/swift-rs/src-rs/swift_ret.rs b/swift-rs/src-rs/swift_ret.rs deleted file mode 100644 index d853d12..0000000 --- a/swift-rs/src-rs/swift_ret.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::{swift::SwiftObject, *}; -use std::ffi::c_void; - -/// Identifies a type as being a valid return type from a Swift function. -/// For types that are objects which need extra retains, -/// the [`retain`](SwiftRet::retain) function will be re-implemented. -pub trait SwiftRet { - /// Adds a retain to the value if possible - /// - /// # Safety - /// Just don't use this. - /// Let [`swift!`] handle it. - unsafe fn retain(&self) {} -} - -macro_rules! primitive_impl { - ($($t:ty),+) => { - $(impl SwiftRet for $t { - })+ - }; -} - -primitive_impl!( - Bool, - Int, - Int8, - Int16, - Int32, - Int64, - UInt, - UInt8, - UInt16, - UInt32, - UInt64, - Float32, - Float64, - *const c_void, - *mut c_void, - *const u8, - () -); - -impl SwiftRet for Option { - unsafe fn retain(&self) { - if let Some(v) = self { - v.retain() - } - } -} - -impl SwiftRet for T { - unsafe fn retain(&self) { - (*self).retain() - } -} diff --git a/swift-rs/src-rs/test-build.rs b/swift-rs/src-rs/test-build.rs deleted file mode 100644 index da43c63..0000000 --- a/swift-rs/src-rs/test-build.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Build script for swift-rs that is a no-op for normal builds, but can be enabled -//! to include test swift library based on env var `TEST_SWIFT_RS=true` with the -//! `build` feature being enabled. - -#[cfg(feature = "build")] -mod build; - -fn main() { - println!("cargo:rerun-if-env-changed=TEST_SWIFT_RS"); - - #[cfg(feature = "build")] - if std::env::var("TEST_SWIFT_RS").unwrap_or_else(|_| "false".into()) == "true" { - use build::SwiftLinker; - - SwiftLinker::new("10.15") - .with_ios("11") - .with_package("test-swift", "tests/swift-pkg") - .link(); - } -} diff --git a/swift-rs/src-rs/types/array.rs b/swift-rs/src-rs/types/array.rs deleted file mode 100644 index fc69069..0000000 --- a/swift-rs/src-rs/types/array.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::{ops::Deref, ptr::NonNull}; - -use crate::swift::SwiftObject; - -use super::SRObject; - -/// Wrapper of [`SRArray`] exclusively for arrays of objects. -/// Equivalent to `SRObjectArray` in Swift. -// SRArray is wrapped in SRObject since the Swift implementation extends NSObject -pub type SRObjectArray = SRObject>>; - -#[doc(hidden)] -#[repr(C)] -pub struct SRArrayImpl { - data: NonNull, - length: usize, -} - -/// General array type for objects and scalars. -/// -/// ## Returning Directly -/// -/// When returning an `SRArray` from a Swift function, -/// you will need to wrap it in an `NSObject` class since -/// Swift doesn't permit returning generic types from `@_cdecl` functions. -/// To account for the wrapping `NSObject`, the array must be wrapped -/// in `SRObject` on the Rust side. -/// -/// ```rust -/// use swift_rs::{swift, SRArray, SRObject, Int}; -/// -/// swift!(fn get_int_array() -> SRObject>); -/// -/// let array = unsafe { get_int_array() }; -/// -/// assert_eq!(array.as_slice(), &[1, 2, 3]) -/// ``` -/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L19) -/// -/// ## Returning in a Struct fIeld -/// -/// When returning an `SRArray` from a custom struct that is itself an `NSObject`, -/// the above work is already done for you. -/// Assuming your custom struct is already wrapped in `SRObject` in Rust, -/// `SRArray` will work normally. -/// -/// ```rust -/// use swift_rs::{swift, SRArray, SRObject, Int}; -/// -/// #[repr(C)] -/// struct ArrayStruct { -/// array: SRArray -/// } -/// -/// swift!(fn get_array_struct() -> SRObject); -/// -/// let data = unsafe { get_array_struct() }; -/// -/// assert_eq!(data.array.as_slice(), &[4, 5, 6]); -/// ``` -/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L32) -#[repr(transparent)] -pub struct SRArray(SRObject>); - -impl SRArray { - pub fn as_slice(&self) -> &[T] { - self.0.as_slice() - } -} - -impl SwiftObject for SRArray { - type Shape = SRArrayImpl; - - fn get_object(&self) -> &SRObject { - &self.0 - } -} - -impl Deref for SRArray { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - self.0.as_slice() - } -} - -impl SRArrayImpl { - pub fn as_slice(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.data.as_ref(), self.length) } - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for SRArray -where - T: serde::Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - use serde::ser::SerializeSeq; - - let mut seq = serializer.serialize_seq(Some(self.len()))?; - for item in self.iter() { - seq.serialize_element(item)?; - } - seq.end() - } -} diff --git a/swift-rs/src-rs/types/data.rs b/swift-rs/src-rs/types/data.rs deleted file mode 100644 index e982235..0000000 --- a/swift-rs/src-rs/types/data.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::{ - swift::{self, SwiftObject}, - Int, -}; - -use super::{array::SRArray, SRObject}; - -use std::ops::Deref; - -type Data = SRArray; - -/// Convenience type for working with byte buffers, -/// analagous to `SRData` in Swift. -/// -/// ```rust -/// use swift_rs::{swift, SRData}; -/// -/// swift!(fn get_data() -> SRData); -/// -/// let data = unsafe { get_data() }; -/// -/// assert_eq!(data.as_ref(), &[1, 2, 3]) -/// ``` -/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L68) -#[repr(transparent)] -pub struct SRData(SRObject); - -impl SRData { - /// - pub fn as_slice(&self) -> &[u8] { - self - } - - pub fn to_vec(&self) -> Vec { - self.as_slice().to_vec() - } -} - -impl SwiftObject for SRData { - type Shape = Data; - - fn get_object(&self) -> &SRObject { - &self.0 - } -} - -impl Deref for SRData { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl AsRef<[u8]> for SRData { - fn as_ref(&self) -> &[u8] { - self - } -} - -impl From<&[u8]> for SRData { - fn from(value: &[u8]) -> Self { - unsafe { swift::data_from_bytes(value.as_ptr(), value.len() as Int) } - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for SRData { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(self) - } -} diff --git a/swift-rs/src-rs/types/mod.rs b/swift-rs/src-rs/types/mod.rs deleted file mode 100644 index 90d9465..0000000 --- a/swift-rs/src-rs/types/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod array; -mod data; -mod object; -mod scalars; -mod string; - -pub use array::*; -pub use data::*; -pub use object::*; -pub use scalars::*; -pub use string::*; diff --git a/swift-rs/src-rs/types/object.rs b/swift-rs/src-rs/types/object.rs deleted file mode 100644 index 49748a7..0000000 --- a/swift-rs/src-rs/types/object.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::swift::{self, SwiftObject}; -use std::{ffi::c_void, ops::Deref, ptr::NonNull}; - -#[doc(hidden)] -#[repr(C)] -pub struct SRObjectImpl { - _nsobject_offset: u8, - data: T, -} - -/// Wrapper for arbitrary `NSObject` types. -/// -/// When returning an `NSObject`, its Rust type must be wrapped in `SRObject`. -/// The type must also be annotated with `#[repr(C)]` to ensure its memory layout -/// is identical to its Swift counterpart's. -/// -/// ```rust -/// use swift_rs::{swift, SRObject, Int, Bool}; -/// -/// #[repr(C)] -/// struct CustomObject { -/// a: Int, -/// b: Bool -/// } -/// -/// swift!(fn get_custom_object() -> SRObject); -/// -/// let value = unsafe { get_custom_object() }; -/// -/// let reference: &CustomObject = value.as_ref(); -/// ``` -/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L49) -#[repr(transparent)] -pub struct SRObject(pub(crate) NonNull>); - -impl SwiftObject for SRObject { - type Shape = T; - - fn get_object(&self) -> &SRObject { - self - } -} - -impl Deref for SRObject { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &self.0.as_ref().data } - } -} - -impl AsRef for SRObject { - fn as_ref(&self) -> &T { - self - } -} - -impl Drop for SRObject { - fn drop(&mut self) { - unsafe { swift::release_object(self.0.as_ref() as *const _ as *const c_void) } - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for SRObject -where - T: serde::Serialize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.deref().serialize(serializer) - } -} diff --git a/swift-rs/src-rs/types/scalars.rs b/swift-rs/src-rs/types/scalars.rs deleted file mode 100644 index 226f3bd..0000000 --- a/swift-rs/src-rs/types/scalars.rs +++ /dev/null @@ -1,34 +0,0 @@ -/// Swift's [`Bool`](https://developer.apple.com/documentation/swift/bool) type -pub type Bool = bool; - -/// Swift's [`Int`](https://developer.apple.com/documentation/swift/int) type -pub type Int = isize; -/// Swift's [`Int8`](https://developer.apple.com/documentation/swift/int8) type -pub type Int8 = i8; -/// Swift's [`Int16`](https://developer.apple.com/documentation/swift/int16) type -pub type Int16 = i16; -/// Swift's [`Int32`](https://developer.apple.com/documentation/swift/int32) type -pub type Int32 = i32; -/// Swift's [`Int64`](https://developer.apple.com/documentation/swift/int64) type -pub type Int64 = i64; - -/// Swift's [`UInt`](https://developer.apple.com/documentation/swift/uint) type -pub type UInt = usize; -/// Swift's [`UInt8`](https://developer.apple.com/documentation/swift/uint8) type -pub type UInt8 = u8; -/// Swift's [`UInt16`](https://developer.apple.com/documentation/swift/uint16) type -pub type UInt16 = u16; -/// Swift's [`UInt32`](https://developer.apple.com/documentation/swift/uint32) type -pub type UInt32 = u32; -/// Swift's [`UInt64`](https://developer.apple.com/documentation/swift/uint64) type -pub type UInt64 = u64; - -/// Swift's [`Float`](https://developer.apple.com/documentation/swift/float) type -pub type Float = f32; -/// Swift's [`Double`](https://developer.apple.com/documentation/swift/double) type -pub type Double = f64; - -/// Swift's [`Float32`](https://developer.apple.com/documentation/swift/float32) type -pub type Float32 = f32; -/// Swift's [`Float64`](https://developer.apple.com/documentation/swift/float64) type -pub type Float64 = f64; diff --git a/swift-rs/src-rs/types/string.rs b/swift-rs/src-rs/types/string.rs deleted file mode 100644 index 3f6f86f..0000000 --- a/swift-rs/src-rs/types/string.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::{ - fmt::{Display, Error, Formatter}, - ops::Deref, -}; - -use crate::{ - swift::{self, SwiftObject}, - Int, SRData, SRObject, -}; - -/// String type that can be shared between Swift and Rust. -/// -/// ```rust -/// use swift_rs::{swift, SRString}; -/// -/// swift!(fn get_greeting(name: &SRString) -> SRString); -/// -/// let greeting = unsafe { get_greeting(&"Brendan".into()) }; -/// -/// assert_eq!(greeting.as_str(), "Hello Brendan!"); -/// ``` -/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L56) -#[repr(transparent)] -pub struct SRString(SRData); - -impl SRString { - pub fn as_str(&self) -> &str { - unsafe { std::str::from_utf8_unchecked(&self.0) } - } -} - -impl SwiftObject for SRString { - type Shape = ::Shape; - - fn get_object(&self) -> &SRObject { - self.0.get_object() - } -} - -impl Deref for SRString { - type Target = str; - - fn deref(&self) -> &Self::Target { - self.as_str() - } -} - -impl AsRef<[u8]> for SRString { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl From<&str> for SRString { - fn from(string: &str) -> Self { - unsafe { swift::string_from_bytes(string.as_ptr(), string.len() as Int) } - } -} - -impl Display for SRString { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - self.as_str().fmt(f) - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for SRString { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(self.as_str()) - } -} -#[cfg(feature = "serde")] -impl<'a> serde::Deserialize<'a> for SRString { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'a>, - { - let string = String::deserialize(deserializer)?; - Ok(SRString::from(string.as_str())) - } -} diff --git a/swift-rs/src-swift/lib.swift b/swift-rs/src-swift/lib.swift deleted file mode 100644 index 7bee3ad..0000000 --- a/swift-rs/src-swift/lib.swift +++ /dev/null @@ -1,94 +0,0 @@ -import Foundation - -public class SRArray: NSObject { - // Used by Rust - let pointer: UnsafePointer - let length: Int; - - // Actual array, deallocates objects inside automatically - let array: [T]; - - public override init() { - self.array = []; - self.pointer = UnsafePointer(self.array); - self.length = 0; - } - - public init(_ data: [T]) { - self.array = data; - self.pointer = UnsafePointer(self.array) - self.length = data.count - } - - public func toArray() -> [T] { - return Array(self.array) - } -} - -public class SRObjectArray: NSObject { - let data: SRArray - - public init(_ data: [NSObject]) { - self.data = SRArray(data) - } -} - -public class SRData: NSObject { - let data: SRArray - - public override init() { - self.data = SRArray() - } - - public init(_ data: [UInt8]) { - self.data = SRArray(data) - } - - public init (_ srArray: SRArray) { - self.data = srArray - } - - public func toArray() -> [UInt8] { - return self.data.toArray() - } -} - -public class SRString: SRData { - public override init() { - super.init([]) - } - - public init(_ string: String) { - super.init(Array(string.utf8)) - } - - init(_ data: SRData) { - super.init(data.data) - } - - public func toString() -> String { - return String(bytes: self.data.array, encoding: .utf8)! - } -} - -@_cdecl("retain_object") -func retainObject(ptr: UnsafeMutableRawPointer) { - let _ = Unmanaged.fromOpaque(ptr).retain() -} - -@_cdecl("release_object") -func releaseObject(ptr: UnsafeMutableRawPointer) { - let _ = Unmanaged.fromOpaque(ptr).release() -} - -@_cdecl("data_from_bytes") -func dataFromBytes(data: UnsafePointer, size: Int) -> SRData { - let buffer = UnsafeBufferPointer(start: data, count: size) - return SRData(Array(buffer)) -} - -@_cdecl("string_from_bytes") -func stringFromBytes(data: UnsafePointer, size: Int) -> SRString { - let data = dataFromBytes(data: data, size: size); - return SRString(data) -}