feat: v1.11.0, remove swiftc dependency
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -511,7 +511,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.10.21"
|
version = "1.11.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-stream",
|
"aes-gcm-stream",
|
||||||
"authenticator 0.3.1",
|
"authenticator 0.3.1",
|
||||||
@@ -549,7 +549,7 @@ dependencies = [
|
|||||||
"spki 0.7.3",
|
"spki 0.7.3",
|
||||||
"ssh-agent",
|
"ssh-agent",
|
||||||
"sshcerts",
|
"sshcerts",
|
||||||
"swift-rs",
|
"swift-secure-enclave-tool-rs",
|
||||||
"tabled",
|
"tabled",
|
||||||
"u2f",
|
"u2f",
|
||||||
"x509",
|
"x509",
|
||||||
@@ -3688,14 +3688,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swift-rs"
|
name = "swift-secure-enclave-tool-rs"
|
||||||
version = "1.0.7"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7"
|
checksum = "1cc8ba06af10bf59b2f47fb46238a0c62c2ac927184253b4fb998b3b3f68950a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.22.1",
|
||||||
"serde",
|
"hex",
|
||||||
"serde_json",
|
"rust_util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@@ -1,13 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.10.21"
|
version = "1.11.0"
|
||||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["with-sequoia-openpgp", "with-secure-enclave"]
|
default = ["with-sequoia-openpgp"]
|
||||||
with-sequoia-openpgp = ["sequoia-openpgp", "openpgp-card-sequoia"]
|
with-sequoia-openpgp = ["sequoia-openpgp", "openpgp-card-sequoia"]
|
||||||
with-secure-enclave = ["swift-rs"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
authenticator = "0.3"
|
authenticator = "0.3"
|
||||||
@@ -50,15 +49,12 @@ rpassword = "7.3"
|
|||||||
secrecy = "0.8"
|
secrecy = "0.8"
|
||||||
der-parser = "9.0"
|
der-parser = "9.0"
|
||||||
sshcerts = "0.13"
|
sshcerts = "0.13"
|
||||||
swift-rs = { version = "1.0.7", optional = true }
|
|
||||||
regex = "1.4.6"
|
regex = "1.4.6"
|
||||||
aes-gcm-stream = "0.2.4"
|
aes-gcm-stream = "0.2.4"
|
||||||
|
swift-secure-enclave-tool-rs = "0.1.0"
|
||||||
#lazy_static = "1.4.0"
|
#lazy_static = "1.4.0"
|
||||||
#ssh-key = "0.4.0"
|
#ssh-key = "0.4.0"
|
||||||
#ctap-hid-fido2 = "2.1.3"
|
#ctap-hid-fido2 = "2.1.3"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
swift-rs = { version = "1.0.7", features = ["build"], optional = true }
|
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
u2f = { git = "https://github.com/jht5945/u2f-rs.git" }
|
u2f = { git = "https://github.com/jht5945/u2f-rs.git" }
|
||||||
|
|||||||
9
build.rs
9
build.rs
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
use crate::ecdsautil;
|
use crate::ecdsautil;
|
||||||
use crate::keyutil::{parse_key_uri, KeyUri};
|
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
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_clap::{Command, CommandError};
|
||||||
use rust_util::util_msg;
|
use rust_util::util_msg;
|
||||||
use spki::EncodePublicKey;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub struct CommandImpl;
|
pub struct CommandImpl;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ impl Command for CommandImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ciphertext = sub_arg_matches.value_of("ciphertext").unwrap();
|
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 {
|
if json_output {
|
||||||
let mut json = BTreeMap::<&'_ str, String>::new();
|
let mut json = BTreeMap::<&'_ str, String>::new();
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ use rust_util::util_msg;
|
|||||||
use crate::ecdsautil::parse_ecdsa_r_and_s;
|
use crate::ecdsautil::parse_ecdsa_r_and_s;
|
||||||
use crate::util::try_decode;
|
use crate::util::try_decode;
|
||||||
|
|
||||||
const SEPARATOR: &str = ".";
|
|
||||||
|
|
||||||
pub struct CommandImpl;
|
pub struct CommandImpl;
|
||||||
|
|
||||||
impl Command for CommandImpl {
|
impl Command for CommandImpl {
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ impl Command for CommandImpl {
|
|||||||
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
|
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
|
||||||
debugging!("Secure enclave key URI: {:?}", se_key_uri);
|
debugging!("Secure enclave key URI: {:?}", se_key_uri);
|
||||||
|
|
||||||
let ephemeral_public_key_der_bytes;
|
let ephemeral_public_key_der_bytes = if epk.starts_with("04") {
|
||||||
if epk.starts_with("04") {
|
|
||||||
let ephemeral_public_key_point_bytes = opt_result!(
|
let ephemeral_public_key_point_bytes = opt_result!(
|
||||||
hex::decode(epk),
|
hex::decode(epk),
|
||||||
"Decode public key point from hex failed: {}"
|
"Decode public key point from hex failed: {}"
|
||||||
@@ -65,11 +64,10 @@ impl Command for CommandImpl {
|
|||||||
return simple_error!("Parse public key failed.");
|
return simple_error!("Parse public key failed.");
|
||||||
}
|
}
|
||||||
let public_key = public_key_opt.unwrap();
|
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 {
|
} 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(
|
let dh = seutil::secure_enclave_p256_dh(
|
||||||
&se_key_uri.private_key,
|
&se_key_uri.private_key,
|
||||||
|
|||||||
@@ -7,11 +7,8 @@ use jwt::{AlgorithmType, Header, ToBase64};
|
|||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use rust_util::{util_msg, util_time, XResult};
|
use rust_util::{util_msg, util_time, XResult};
|
||||||
use serde_json::{Map, Number, Value};
|
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, rsautil, util};
|
||||||
use crate::{cmd_signjwt, digest, ecdsautil, hmacutil, pivutil, rsautil, util};
|
|
||||||
|
|
||||||
const SEPARATOR: &str = ".";
|
const SEPARATOR: &str = ".";
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ pub fn parse_ecdsa_to_rs(signature_der: &[u8]) -> XResult<Vec<u8>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_ecdsa_r_and_s(signature_der: &[u8]) -> XResult<(Vec<u8>, Vec<u8>)> {
|
pub fn parse_ecdsa_r_and_s(signature_der: &[u8]) -> XResult<(Vec<u8>, Vec<u8>)> {
|
||||||
let mut vec_r: Vec<u8> = Vec::new();
|
let vec_r: Vec<u8>;
|
||||||
let mut vec_s: Vec<u8> = Vec::new();
|
let vec_s: Vec<u8>;
|
||||||
let (_, parsed_signature) = opt_result!(der_parser::parse_der(signature_der), "Parse signature failed: {}");
|
let (_, parsed_signature) = opt_result!(der_parser::parse_der(signature_der), "Parse signature failed: {}");
|
||||||
match parsed_signature.content {
|
match parsed_signature.content {
|
||||||
BerObjectContent::Sequence(seq) => {
|
BerObjectContent::Sequence(seq) => {
|
||||||
@@ -73,7 +73,6 @@ pub fn generate_p384_keypair() -> XResult<(String, String, String)> {
|
|||||||
|
|
||||||
macro_rules! parse_ecdsa_private_key {
|
macro_rules! parse_ecdsa_private_key {
|
||||||
($algo: tt, $parse_ecdsa_private_key: tt) => ({
|
($algo: tt, $parse_ecdsa_private_key: tt) => ({
|
||||||
use $algo::ecdsa::{SigningKey};
|
|
||||||
use $algo::pkcs8::DecodePrivateKey;
|
use $algo::pkcs8::DecodePrivateKey;
|
||||||
use $algo::SecretKey;
|
use $algo::SecretKey;
|
||||||
|
|
||||||
@@ -103,7 +102,7 @@ pub fn sign_p256_rs(private_key_d: &[u8], pre_hash: &[u8]) -> XResult<Vec<u8>> {
|
|||||||
use p256::ecdsa::{SigningKey, Signature};
|
use p256::ecdsa::{SigningKey, Signature};
|
||||||
use p256::ecdsa::signature::hazmat::PrehashSigner;
|
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)?;
|
let signature: Signature = signing_key.sign_prehash(pre_hash)?;
|
||||||
|
|
||||||
Ok(signature.to_bytes().to_vec())
|
Ok(signature.to_bytes().to_vec())
|
||||||
@@ -113,7 +112,7 @@ pub fn sign_p384_rs(private_key_d: &[u8], pre_hash: &[u8]) -> XResult<Vec<u8>> {
|
|||||||
use p384::ecdsa::{SigningKey, Signature};
|
use p384::ecdsa::{SigningKey, Signature};
|
||||||
use p384::ecdsa::signature::hazmat::PrehashSigner;
|
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)?;
|
let signature: Signature = signing_key.sign_prehash(pre_hash)?;
|
||||||
|
|
||||||
Ok(signature.to_bytes().to_vec())
|
Ok(signature.to_bytes().to_vec())
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use yubico_manager::config::{Config, Mode, Slot};
|
|||||||
use yubico_manager::hmacmode::HmacKey;
|
use yubico_manager::hmacmode::HmacKey;
|
||||||
use yubico_manager::sec::hmac_sha1;
|
use yubico_manager::sec::hmac_sha1;
|
||||||
use yubico_manager::Yubico;
|
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};
|
use crate::util::{base64_decode, base64_encode};
|
||||||
|
|
||||||
const HMAC_ENC_PREFIX: &str = "hmac_enc:";
|
const HMAC_ENC_PREFIX: &str = "hmac_enc:";
|
||||||
@@ -33,8 +33,8 @@ pub fn hmac_encrypt(plaintext: &[u8]) -> XResult<String> {
|
|||||||
|
|
||||||
Ok(format!("{}{}:{}:{}",
|
Ok(format!("{}{}:{}:{}",
|
||||||
HMAC_ENC_PREFIX,
|
HMAC_ENC_PREFIX,
|
||||||
hex::encode(&hmac_nonce),
|
hex::encode(hmac_nonce),
|
||||||
hex::encode(&aes_gcm_nonce),
|
hex::encode(aes_gcm_nonce),
|
||||||
base64_encode(&ciphertext)
|
base64_encode(&ciphertext)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ pub fn compute_yubikey_hmac(challenge_bytes: &[u8]) -> XResult<Vec<u8>> {
|
|||||||
.set_mode(Mode::Sha1)
|
.set_mode(Mode::Sha1)
|
||||||
.set_slot(Slot::Slot2);
|
.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())
|
Ok(hmac_result.deref().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@@ -33,15 +33,10 @@ mod cmd_pivverify;
|
|||||||
mod cmd_rsadecrypt;
|
mod cmd_rsadecrypt;
|
||||||
mod cmd_rsaencrypt;
|
mod cmd_rsaencrypt;
|
||||||
mod cmd_rsaverify;
|
mod cmd_rsaverify;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
mod cmd_se;
|
mod cmd_se;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
mod cmd_se_ecdh;
|
mod cmd_se_ecdh;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
mod cmd_se_ecsign;
|
mod cmd_se_ecsign;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
mod cmd_se_generate;
|
mod cmd_se_generate;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
mod cmd_se_recover;
|
mod cmd_se_recover;
|
||||||
mod cmd_signfile;
|
mod cmd_signfile;
|
||||||
mod cmd_signjwt;
|
mod cmd_signjwt;
|
||||||
@@ -68,7 +63,6 @@ mod pinutil;
|
|||||||
mod pivutil;
|
mod pivutil;
|
||||||
mod pkiutil;
|
mod pkiutil;
|
||||||
mod rsautil;
|
mod rsautil;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
mod seutil;
|
mod seutil;
|
||||||
mod signfile;
|
mod signfile;
|
||||||
mod sshutil;
|
mod sshutil;
|
||||||
@@ -139,15 +133,10 @@ fn inner_main() -> CommandError {
|
|||||||
Box::new(cmd_signjwtsoft::CommandImpl),
|
Box::new(cmd_signjwtsoft::CommandImpl),
|
||||||
Box::new(cmd_signfile::CommandImpl),
|
Box::new(cmd_signfile::CommandImpl),
|
||||||
Box::new(cmd_verifyfile::CommandImpl),
|
Box::new(cmd_verifyfile::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
Box::new(cmd_se::CommandImpl),
|
Box::new(cmd_se::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
Box::new(cmd_se_generate::CommandImpl),
|
Box::new(cmd_se_generate::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
Box::new(cmd_se_recover::CommandImpl),
|
Box::new(cmd_se_recover::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
Box::new(cmd_se_ecsign::CommandImpl),
|
Box::new(cmd_se_ecsign::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
Box::new(cmd_se_ecdh::CommandImpl),
|
Box::new(cmd_se_ecdh::CommandImpl),
|
||||||
Box::new(cmd_ecverify::CommandImpl),
|
Box::new(cmd_ecverify::CommandImpl),
|
||||||
Box::new(cmd_parseecdsasignature::CommandImpl),
|
Box::new(cmd_parseecdsasignature::CommandImpl),
|
||||||
@@ -159,8 +148,6 @@ fn inner_main() -> CommandError {
|
|||||||
let mut features: Vec<&str> = vec![];
|
let mut features: Vec<&str> = vec![];
|
||||||
#[cfg(feature = "with-sequoia-openpgp")]
|
#[cfg(feature = "with-sequoia-openpgp")]
|
||||||
features.push("sequoia-openpgp");
|
features.push("sequoia-openpgp");
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
|
||||||
features.push("secure-enclave");
|
|
||||||
features
|
features
|
||||||
};
|
};
|
||||||
let about = format!(
|
let about = format!(
|
||||||
|
|||||||
126
src/seutil.rs
126
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 rust_util::XResult;
|
||||||
use swift_rs::swift;
|
use swift_secure_enclave_tool_rs::KeyPurpose;
|
||||||
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);
|
|
||||||
|
|
||||||
pub fn is_support_se() -> bool {
|
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<u8>, Vec<u8>, String)> {
|
pub fn generate_secure_enclave_p256_keypair(sign: bool) -> XResult<(Vec<u8>, Vec<u8>, String)> {
|
||||||
let p256_keypair_result = if sign {
|
let key_material = if sign {
|
||||||
unsafe { generate_secure_enclave_p256_ecsign_keypair() }
|
swift_secure_enclave_tool_rs::generate_ecdsa_keypair(KeyPurpose::Signing, true)?
|
||||||
} else {
|
} 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(
|
pub fn recover_secure_enclave_p256_public_key(
|
||||||
private_key: &str,
|
private_key: &str,
|
||||||
sign: bool,
|
sign: bool,
|
||||||
) -> XResult<(Vec<u8>, Vec<u8>, String)> {
|
) -> XResult<(Vec<u8>, Vec<u8>, String)> {
|
||||||
let p256_keypair_result = if sign {
|
let private_key_representation = STANDARD.decode(private_key)?;
|
||||||
unsafe { recover_secure_enclave_p256_ecsign_public_key(SRString::from(private_key)) }
|
let key_material = if sign {
|
||||||
|
swift_secure_enclave_tool_rs::recover_ecdsa_keypair(
|
||||||
|
KeyPurpose::Signing,
|
||||||
|
&private_key_representation,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
unsafe { recover_secure_enclave_p256_ecdh_public_key(SRString::from(private_key)) }
|
swift_secure_enclave_tool_rs::recover_ecdsa_keypair(
|
||||||
};
|
KeyPurpose::KeyAgreement,
|
||||||
parse_p256_keypair_result(p256_keypair_result.as_str())
|
&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(
|
pub fn secure_enclave_p256_dh(
|
||||||
private_key: &str,
|
private_key: &str,
|
||||||
ephemeral_public_key_bytes: &[u8],
|
ephemeral_public_key_bytes: &[u8],
|
||||||
) -> XResult<Vec<u8>> {
|
) -> XResult<Vec<u8>> {
|
||||||
let dh_result = unsafe {
|
let private_key_representation = STANDARD.decode(private_key)?;
|
||||||
compute_secure_enclave_p256_ecdh(
|
let shared_secret = swift_secure_enclave_tool_rs::private_key_ecdh(
|
||||||
SRString::from(private_key),
|
&private_key_representation,
|
||||||
SRString::from(base64_encode(ephemeral_public_key_bytes).as_str()),
|
ephemeral_public_key_bytes,
|
||||||
)
|
)?;
|
||||||
};
|
Ok(shared_secret)
|
||||||
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::<String>();
|
|
||||||
let shared_secret_hex = shared_secret_hex.trim();
|
|
||||||
|
|
||||||
Ok(opt_result!(
|
|
||||||
hex::decode(shared_secret_hex),
|
|
||||||
"Decrypt shared secret hex: {}, failed: {}",
|
|
||||||
shared_secret_hex
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn secure_enclave_p256_sign(private_key: &str, content: &[u8]) -> XResult<Vec<u8>> {
|
pub fn secure_enclave_p256_sign(private_key: &str, content: &[u8]) -> XResult<Vec<u8>> {
|
||||||
let signature_result = unsafe {
|
let private_key_representation = STANDARD.decode(private_key)?;
|
||||||
compute_secure_enclave_p256_ecsign(
|
let signature =
|
||||||
SRString::from(private_key),
|
swift_secure_enclave_tool_rs::private_key_ecdsa_sign(&private_key_representation, content)?;
|
||||||
SRString::from(base64_encode(content).as_str()),
|
Ok(signature)
|
||||||
)
|
|
||||||
};
|
|
||||||
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::<String>();
|
|
||||||
debugging!("Signature: {}", &signature);
|
|
||||||
Ok(base64_decode(&signature)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_p256_keypair_result(p256_keypair_result_str: &str) -> XResult<(Vec<u8>, Vec<u8>, 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::<String>();
|
|
||||||
let public_key_and_private_keys = public_key_and_private_key.split(',').collect::<Vec<_>>();
|
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@@ -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<CFError>? = 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)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
417
swift-rs/Cargo.lock
generated
417
swift-rs/Cargo.lock
generated
@@ -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"
|
|
||||||
@@ -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"]
|
|
||||||
@@ -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.
|
|
||||||
@@ -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.
|
|
||||||
@@ -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")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@@ -1,483 +0,0 @@
|
|||||||
# swift-rs
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
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 <b>all</b> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<sub><sup>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<T>`.
|
|
||||||
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<SquareNumberResult>);
|
|
||||||
```
|
|
||||||
|
|
||||||
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<T>` type!
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use swift_rs::{swift, Bool, SRString};
|
|
||||||
|
|
||||||
swift!(optional_string(return_nil: Bool) -> Option<SRString>)
|
|
||||||
```
|
|
||||||
|
|
||||||
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<T>`) 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<T>` 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<Int>
|
|
||||||
|
|
||||||
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<Int>
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since IntArray extends NSObject in its Swift implementation,
|
|
||||||
// it must be wrapped in SRObject on the Rust side
|
|
||||||
swift!(fn get_numbers() -> SRObject<IntArray>);
|
|
||||||
|
|
||||||
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<usize>`,
|
|
||||||
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<SRArray<Int>>);
|
|
||||||
```
|
|
||||||
|
|
||||||
**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<T>` 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<T>`, 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<T> since
|
|
||||||
// SRObjectArray<T> does it automatically
|
|
||||||
swift!(fn get_tuples() -> SRObjectArray<IntTuple>);
|
|
||||||
|
|
||||||
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<T>` you like, just remember to follow the 3 rules!
|
|
||||||
|
|
||||||
## Bonuses
|
|
||||||
|
|
||||||
### SRData
|
|
||||||
|
|
||||||
A wrapper type for `SRArray<T>` 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.
|
|
||||||
@@ -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
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
@@ -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<String>,
|
|
||||||
runtime_library_import_paths: Vec<String>,
|
|
||||||
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<SwiftPackage>,
|
|
||||||
macos_min_version: String,
|
|
||||||
ios_min_version: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Path>) -> 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");
|
|
||||||
}
|
|
||||||
@@ -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<ValueArity>> {
|
|
||||||
// v.into_iter().fold(
|
|
||||||
// BTreeMap::<_, Vec<ValueArity>>::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::<Vec<_>>();
|
|
||||||
|
|
||||||
// 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::<Vec<_>>();
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
@@ -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::*;
|
|
||||||
@@ -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<T::Shape>);
|
|
||||||
|
|
||||||
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<Self::Shape>;
|
|
||||||
|
|
||||||
/// 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<Self>
|
|
||||||
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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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<T>, SRArray<T>, 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<T: SwiftObject> SwiftRet for Option<T> {
|
|
||||||
unsafe fn retain(&self) {
|
|
||||||
if let Some(v) = self {
|
|
||||||
v.retain()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SwiftObject> SwiftRet for T {
|
|
||||||
unsafe fn retain(&self) {
|
|
||||||
(*self).retain()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<T> = SRObject<SRArray<SRObject<T>>>;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SRArrayImpl<T> {
|
|
||||||
data: NonNull<T>,
|
|
||||||
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<SRArray<Int>>);
|
|
||||||
///
|
|
||||||
/// 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<Int>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// swift!(fn get_array_struct() -> SRObject<ArrayStruct>);
|
|
||||||
///
|
|
||||||
/// 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<T>(SRObject<SRArrayImpl<T>>);
|
|
||||||
|
|
||||||
impl<T> SRArray<T> {
|
|
||||||
pub fn as_slice(&self) -> &[T] {
|
|
||||||
self.0.as_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> SwiftObject for SRArray<T> {
|
|
||||||
type Shape = SRArrayImpl<T>;
|
|
||||||
|
|
||||||
fn get_object(&self) -> &SRObject<Self::Shape> {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for SRArray<T> {
|
|
||||||
type Target = [T];
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0.as_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> SRArrayImpl<T> {
|
|
||||||
pub fn as_slice(&self) -> &[T] {
|
|
||||||
unsafe { std::slice::from_raw_parts(self.data.as_ref(), self.length) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<T> serde::Serialize for SRArray<T>
|
|
||||||
where
|
|
||||||
T: serde::Serialize,
|
|
||||||
{
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
swift::{self, SwiftObject},
|
|
||||||
Int,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{array::SRArray, SRObject};
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
type Data = SRArray<u8>;
|
|
||||||
|
|
||||||
/// 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<Data>);
|
|
||||||
|
|
||||||
impl SRData {
|
|
||||||
///
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_vec(&self) -> Vec<u8> {
|
|
||||||
self.as_slice().to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SwiftObject for SRData {
|
|
||||||
type Shape = Data;
|
|
||||||
|
|
||||||
fn get_object(&self) -> &SRObject<Self::Shape> {
|
|
||||||
&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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_bytes(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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::*;
|
|
||||||
@@ -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<T> {
|
|
||||||
_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<CustomObject>);
|
|
||||||
///
|
|
||||||
/// 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<T>(pub(crate) NonNull<SRObjectImpl<T>>);
|
|
||||||
|
|
||||||
impl<T> SwiftObject for SRObject<T> {
|
|
||||||
type Shape = T;
|
|
||||||
|
|
||||||
fn get_object(&self) -> &SRObject<Self::Shape> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for SRObject<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
unsafe { &self.0.as_ref().data }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsRef<T> for SRObject<T> {
|
|
||||||
fn as_ref(&self) -> &T {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Drop for SRObject<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { swift::release_object(self.0.as_ref() as *const _ as *const c_void) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<T> serde::Serialize for SRObject<T>
|
|
||||||
where
|
|
||||||
T: serde::Serialize,
|
|
||||||
{
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
self.deref().serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -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 = <SRData as SwiftObject>::Shape;
|
|
||||||
|
|
||||||
fn get_object(&self) -> &SRObject<Self::Shape> {
|
|
||||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
serializer.serialize_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'a> serde::Deserialize<'a> for SRString {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'a>,
|
|
||||||
{
|
|
||||||
let string = String::deserialize(deserializer)?;
|
|
||||||
Ok(SRString::from(string.as_str()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
public class SRArray<T>: NSObject {
|
|
||||||
// Used by Rust
|
|
||||||
let pointer: UnsafePointer<T>
|
|
||||||
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<NSObject>
|
|
||||||
|
|
||||||
public init(_ data: [NSObject]) {
|
|
||||||
self.data = SRArray(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SRData: NSObject {
|
|
||||||
let data: SRArray<UInt8>
|
|
||||||
|
|
||||||
public override init() {
|
|
||||||
self.data = SRArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(_ data: [UInt8]) {
|
|
||||||
self.data = SRArray(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init (_ srArray: SRArray<UInt8>) {
|
|
||||||
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<AnyObject>.fromOpaque(ptr).retain()
|
|
||||||
}
|
|
||||||
|
|
||||||
@_cdecl("release_object")
|
|
||||||
func releaseObject(ptr: UnsafeMutableRawPointer) {
|
|
||||||
let _ = Unmanaged<AnyObject>.fromOpaque(ptr).release()
|
|
||||||
}
|
|
||||||
|
|
||||||
@_cdecl("data_from_bytes")
|
|
||||||
func dataFromBytes(data: UnsafePointer<UInt8>, size: Int) -> SRData {
|
|
||||||
let buffer = UnsafeBufferPointer(start: data, count: size)
|
|
||||||
return SRData(Array(buffer))
|
|
||||||
}
|
|
||||||
|
|
||||||
@_cdecl("string_from_bytes")
|
|
||||||
func stringFromBytes(data: UnsafePointer<UInt8>, size: Int) -> SRString {
|
|
||||||
let data = dataFromBytes(data: data, size: size);
|
|
||||||
return SRString(data)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user