feat: v1.10.9, add se supprot
This commit is contained in:
250
Cargo.lock
generated
250
Cargo.lock
generated
@@ -71,9 +71,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.93"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
@@ -103,7 +103,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"rusticata-macros 4.1.0",
|
"rusticata-macros 4.1.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.36",
|
"time 0.3.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -420,9 +420,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "buffered-reader"
|
name = "buffered-reader"
|
||||||
version = "1.3.1"
|
version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd098763fdb64579407a8c83cf0d751e6d4a7e161d0114c89cc181a2ca760ec8"
|
checksum = "fabd1c5e55587a8e8526172d63ad2ba665fa18c8acb39ec9a77af1708c982b9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"flate2",
|
"flate2",
|
||||||
@@ -460,15 +460,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.8.0"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bzip2"
|
name = "bzip2"
|
||||||
version = "0.4.4"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
checksum = "bafdbf26611df8c14810e268ddceda071c297570a5fb360ceddf617fe417ef58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bzip2-sys",
|
"bzip2-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -487,7 +487,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.10.8"
|
version = "1.10.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"authenticator 0.3.1",
|
"authenticator 0.3.1",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
@@ -523,6 +523,7 @@ dependencies = [
|
|||||||
"spki 0.7.3",
|
"spki 0.7.3",
|
||||||
"ssh-agent",
|
"ssh-agent",
|
||||||
"sshcerts",
|
"sshcerts",
|
||||||
|
"swift-rs",
|
||||||
"tabled",
|
"tabled",
|
||||||
"u2f",
|
"u2f",
|
||||||
"x509",
|
"x509",
|
||||||
@@ -533,9 +534,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.37"
|
version = "1.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf"
|
checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -563,9 +564,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.38"
|
version = "0.4.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
@@ -662,9 +663,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.14"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -1078,19 +1079,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
@@ -1126,9 +1127,9 @@ checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.34"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide 0.8.0",
|
"miniz_oxide 0.8.0",
|
||||||
@@ -1294,9 +1295,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "1.1.0"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a"
|
checksum = "2cb8bc4c28d15ade99c7e90b219f30da4be5c88e586277e8cbe886beeb868ab2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
@@ -1365,7 +1366,7 @@ version = "0.3.26"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -1373,7 +1374,7 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio 1.41.1",
|
"tokio 1.42.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@@ -1386,9 +1387,9 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.1"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@@ -1472,7 +1473,7 @@ version = "0.2.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
@@ -1483,7 +1484,7 @@ version = "0.4.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"http",
|
"http",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
@@ -1512,7 +1513,7 @@ version = "0.14.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
|
checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -1524,7 +1525,7 @@ dependencies = [
|
|||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio 1.41.1",
|
"tokio 1.42.0",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
"want",
|
"want",
|
||||||
@@ -1536,10 +1537,10 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"hyper",
|
"hyper",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio 1.41.1",
|
"tokio 1.42.0",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1707,9 +1708,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@@ -1776,10 +1777,11 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.72"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1865,15 +1867,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.162"
|
version = "0.2.164"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
@@ -1935,9 +1937,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@@ -2058,11 +2060,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.3.9",
|
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@@ -2832,7 +2833,7 @@ checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"pem",
|
"pem",
|
||||||
"ring 0.16.20",
|
"ring 0.16.20",
|
||||||
"time 0.3.36",
|
"time 0.3.37",
|
||||||
"yasna",
|
"yasna",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2892,7 +2893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -2915,7 +2916,7 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio 1.41.1",
|
"tokio 1.42.0",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
@@ -3030,9 +3031,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.6"
|
version = "0.9.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
|
checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
@@ -3129,15 +3130,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.39"
|
version = "0.38.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee"
|
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3172,9 +3173,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.26"
|
version = "0.1.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
|
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@@ -3262,9 +3263,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sequoia-openpgp"
|
name = "sequoia-openpgp"
|
||||||
version = "1.21.2"
|
version = "1.22.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13261ee216b44d932ef93b2d4a75d45199bef77864bcc5b77ecfc7bc0ecb02d6"
|
checksum = "e858e4e9e48ff079cede92e1b45c942a5466ce9a4e3cc0c2a7e66586a718ef59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -3291,9 +3292,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.214"
|
version = "1.0.215"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@@ -3319,9 +3320,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.214"
|
version = "1.0.215"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
@@ -3330,9 +3331,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.132"
|
version = "1.0.133"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3383,7 +3384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b"
|
checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"generic-array 1.1.0",
|
"generic-array 1.1.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3490,9 +3491,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.7"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@@ -3627,6 +3628,17 @@ version = "2.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "swift-rs"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@@ -3783,18 +3795,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.68"
|
version = "1.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.68"
|
version = "1.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
@@ -3814,9 +3826,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.36"
|
version = "0.3.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
@@ -3835,9 +3847,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.18"
|
version = "0.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"time-core",
|
"time-core",
|
||||||
@@ -3909,14 +3921,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.41.1"
|
version = "1.42.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
|
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 1.0.2",
|
"mio 1.0.3",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@@ -3982,7 +3994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio 1.41.1",
|
"tokio 1.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4092,15 +4104,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.12"
|
version = "0.7.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 1.8.0",
|
"bytes 1.9.0",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio 1.41.1",
|
"tokio 1.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4111,9 +4123,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.40"
|
version = "0.1.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
@@ -4121,9 +4133,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.32"
|
version = "0.1.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
@@ -4188,9 +4200,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.3"
|
version = "2.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
|
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
@@ -4275,9 +4287,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -4286,13 +4298,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-backend"
|
name = "wasm-bindgen-backend"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
@@ -4301,21 +4312,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.45"
|
version = "0.4.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -4323,9 +4335,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
@@ -4336,15 +4348,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.95"
|
version = "0.2.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.72"
|
version = "0.3.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -4639,7 +4651,7 @@ dependencies = [
|
|||||||
"oid-registry",
|
"oid-registry",
|
||||||
"rusticata-macros 4.1.0",
|
"rusticata-macros 4.1.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.36",
|
"time 0.3.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4657,7 +4669,7 @@ dependencies = [
|
|||||||
"ring 0.16.20",
|
"ring 0.16.20",
|
||||||
"rusticata-macros 4.1.0",
|
"rusticata-macros 4.1.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.36",
|
"time 0.3.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4672,14 +4684,14 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"time 0.3.36",
|
"time 0.3.37",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.7.4"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
@@ -4689,9 +4701,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke-derive"
|
name = "yoke-derive"
|
||||||
version = "0.7.4"
|
version = "0.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
@@ -4771,7 +4783,7 @@ dependencies = [
|
|||||||
"pbkdf2 0.12.2",
|
"pbkdf2 0.12.2",
|
||||||
"pcsc",
|
"pcsc",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"rsa 0.9.6",
|
"rsa 0.9.7",
|
||||||
"secrecy",
|
"secrecy",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
@@ -4805,18 +4817,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerofrom"
|
name = "zerofrom"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerofrom-derive",
|
"zerofrom-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerofrom-derive"
|
name = "zerofrom-derive"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.37",
|
"quote 1.0.37",
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.10.8"
|
version = "1.10.9"
|
||||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["with-sequoia-openpgp"]
|
default = ["with-sequoia-openpgp", "with-secure-enclave"]
|
||||||
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"
|
||||||
@@ -49,9 +50,13 @@ 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 }
|
||||||
#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" }
|
||||||
|
|||||||
625
README.md
625
README.md
@@ -1,238 +1,483 @@
|
|||||||
# card-cli
|
# swift-rs
|
||||||
|
|
||||||
> FIDO(U2F, WebAuthn), YubiKey, OpenPGP command line tool
|

|
||||||
|

|
||||||
|
|
||||||
Install:
|
Call Swift functions from Rust with ease!
|
||||||
```shell
|
|
||||||
cargo install --git https://git.hatter.ink/hatter/card-cli.git
|
## 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"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
Compile without features:
|
Next, some setup work must be done:
|
||||||
```shell
|
|
||||||
cargo build --release --no-default-features
|
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.
|
||||||
|
|
||||||
# PGP
|
```rust
|
||||||
|
use swift_rs::SwiftLinker;
|
||||||
|
|
||||||
## encrypt & decrypt
|
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();
|
||||||
|
|
||||||
sample encrypt public key
|
// Other build steps
|
||||||
```
|
|
||||||
-----BEGIN PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApUM8M+QRMUw0dIvXISFx
|
|
||||||
43j4h9CK38Y9HD6kPcc3Z0dCGPiFy7Ze0OQebPWHyUZ2YmqsdyzFuOQuV9P2pxxj
|
|
||||||
/WLIgRqZV8Jk8tWhtAjOOvm0MTc2rg+EJHfa+zhX4eFEMsj4DvQBMJDXiKnpXTM/
|
|
||||||
j7oMKpIUQHqfXBwsEJHLmHZTLeEBEYKcZXTAmuu3WdxK5jvEc02Xt2hZ1fBs0M9e
|
|
||||||
/2EMe3t69aH4/rabiBjF2h9Jde15wrJMxXaCCWJqYhbBS0CJ3BdjkAqOIpcqPXva
|
|
||||||
xiJN1pNpK8ejA9Q4Nmx7pxnvfv+hCPkWXZS3r/BWZ9lFZc8uErQEbB4gLgko8jOl
|
|
||||||
fQF7cYqtZEs69qY8nnIUBsqZYfAp+bQd2xCFSbEZAl+OrtGzfVjD9YFMPy02+xRg
|
|
||||||
v2N3KT3KHHvuU7WxrvffrshP2fwDuG2MBlmcq1suAKxA0cYPSyajceEqw/3ogSp7
|
|
||||||
7SYx41rT8EWLmTvU0CHzCsuf/O7sDWZRfxatAzWhBBhnKCPqzizpOQOqm8XhCt74
|
|
||||||
FfnabPpHM9XUjoQIPrTssyS3eWqynzJiAqez6v2LK2fhL7IkcLtvt5p59Y+KY4I6
|
|
||||||
YQ09iUh7lKJHRhkgTomUurJHieVHMWFGIHofEC+nU6pGIUh0P7Nr0Gz45GJTwWGd
|
|
||||||
hW53WfImja+b5kwwyqUikyMCAwEAAQ==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
```
|
|
||||||
|
|
||||||
encrypt
|
|
||||||
```shell
|
|
||||||
$ openssl rsautl -encrypt -pubin -inkey enc_key.pem -in test.txt -out enc.txt -pkcs
|
|
||||||
|
|
||||||
OR
|
|
||||||
|
|
||||||
$ openssl pkeyutl -encrypt -inkey enc_key.pem -pubin -in a.txt -out enc.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
decrypt
|
|
||||||
```shell
|
|
||||||
$ card-cli pgp-card-decrypt -c $(cat enc.txt | xxd -ps -c 11111)
|
|
||||||
|
|
||||||
OR
|
|
||||||
|
|
||||||
$ card-cli piv-decrypt -s r3 -c "$(cat enc.txt | base64)"
|
|
||||||
```
|
|
||||||
|
|
||||||
## sign & verify
|
|
||||||
|
|
||||||
sign
|
|
||||||
```shell
|
|
||||||
$ card-cli pgp-card-sign -2 $(shasum -a 256 test.txt | awk '{print $1}')
|
|
||||||
|
|
||||||
OR
|
|
||||||
|
|
||||||
$ card-cli pgp-card-sign --in test.txt --use-sha256
|
|
||||||
```
|
|
||||||
|
|
||||||
verify
|
|
||||||
```shell
|
|
||||||
$ openssl dgst -sha256 -verify sign_key.pem -signature sig test.txt
|
|
||||||
Verified OK
|
|
||||||
```
|
|
||||||
|
|
||||||
# sample public keys
|
|
||||||
|
|
||||||
```
|
|
||||||
[INFO ] Authentication fingerprint: EB0A43A10BFC6E58323F7650BA42AE533FDCE10E
|
|
||||||
[INFO ] Authentication public key sha256: ac97c7f9f500f3fbab635536096311c62698f8c22abd9e9687de7893932bc15b
|
|
||||||
[INFO ] Authentication public key: -----BEGIN PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA8Kg5fg47YilT/xFOZ7xK
|
|
||||||
17T47cfwzS6L/4IRtTjcygvmOVSdOISihQxVfsygpxhThRQ3pjqhFGqH9LUIpry/
|
|
||||||
a8hWfPMZYolYywBvdx5S6UGDUeRf2zLcRYrQo+Fs9oxdhxPE05HhWl9L5ORn4HWz
|
|
||||||
RZSkNfh7PDKPJRUaJV85uB6Fyvt0GGY14pmINZ7NRLLi2ubYBlp3CLSh7XdleVE8
|
|
||||||
/Q6gya501INhXUksuwHXdPYtcXF3l+VIdMc6YJTxivFLtujqiEAfEwauuv+1GzsN
|
|
||||||
ZDOg6JfSc+1d7iZMixU4RrKtzM57ZwGX0bAK3MQdP6iT20DOYq/BDJTXJuhQBWgE
|
|
||||||
6pIDiTJF4q/If0ZLxU+kxstAEg0fuD+wOg/+4W1BSn5D3hSdvVOxgj3hWtPudAVp
|
|
||||||
QucP8LKnq5B0oy4LdGqXXAQYJ2Q+ln0N9By2T8N/P37HOsR7yJLl8cM2FptCoo4x
|
|
||||||
ViGzmIbir8EyZ6VQmoi8fqOP4x9nH5XeNA2JCVLEc0o6n5PJ4IitYYCb0NGOPTHV
|
|
||||||
FEz2qzxkQDJxS5oC7GddWQB/pa4Jq0EL9dEabB2oPyvYBAmmE0HzZWLl3T1kR1dJ
|
|
||||||
fAXuqgShFcZLXa1SFUpLzlJi3jARuxoaUeHnKP3xeAd8o5WPBwzXM7LL47nTueNa
|
|
||||||
uFZKwHs/e9x4EszQ/qFo2uECAwEAAQ==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
[INFO ] Encryption fingerprint: E48EC98FE6CAE85AAFD5A68AC37A909EAF1BFB00
|
|
||||||
[INFO ] Encryption public key sha256: de5a99c239a82adf039982cb6319abcb95f44cfc76a5027ae6f7819cfc5fde7c
|
|
||||||
[INFO ] Encryption public key: -----BEGIN PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApUM8M+QRMUw0dIvXISFx
|
|
||||||
43j4h9CK38Y9HD6kPcc3Z0dCGPiFy7Ze0OQebPWHyUZ2YmqsdyzFuOQuV9P2pxxj
|
|
||||||
/WLIgRqZV8Jk8tWhtAjOOvm0MTc2rg+EJHfa+zhX4eFEMsj4DvQBMJDXiKnpXTM/
|
|
||||||
j7oMKpIUQHqfXBwsEJHLmHZTLeEBEYKcZXTAmuu3WdxK5jvEc02Xt2hZ1fBs0M9e
|
|
||||||
/2EMe3t69aH4/rabiBjF2h9Jde15wrJMxXaCCWJqYhbBS0CJ3BdjkAqOIpcqPXva
|
|
||||||
xiJN1pNpK8ejA9Q4Nmx7pxnvfv+hCPkWXZS3r/BWZ9lFZc8uErQEbB4gLgko8jOl
|
|
||||||
fQF7cYqtZEs69qY8nnIUBsqZYfAp+bQd2xCFSbEZAl+OrtGzfVjD9YFMPy02+xRg
|
|
||||||
v2N3KT3KHHvuU7WxrvffrshP2fwDuG2MBlmcq1suAKxA0cYPSyajceEqw/3ogSp7
|
|
||||||
7SYx41rT8EWLmTvU0CHzCsuf/O7sDWZRfxatAzWhBBhnKCPqzizpOQOqm8XhCt74
|
|
||||||
FfnabPpHM9XUjoQIPrTssyS3eWqynzJiAqez6v2LK2fhL7IkcLtvt5p59Y+KY4I6
|
|
||||||
YQ09iUh7lKJHRhkgTomUurJHieVHMWFGIHofEC+nU6pGIUh0P7Nr0Gz45GJTwWGd
|
|
||||||
hW53WfImja+b5kwwyqUikyMCAwEAAQ==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
[INFO ] Signature fingerprint: 6FAFC0E0170985AA71545483C794B1646A886CD6
|
|
||||||
[INFO ] Signature public key sha256: d65831b0316a03828eeb31fe6a51e6eec59e7092eb6d3477404ad2f5fa08e903
|
|
||||||
[INFO ] Signature public key: -----BEGIN PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7kVYTHxFjZD9kT+w97B
|
|
||||||
GiHfqlyoulJ10cRqaWwX3/mZKfoeGJkBDglFnLfgtHhXivPqRSn73sCX6M0HCzSq
|
|
||||||
9M/drkms/H8cecM08SoZdZTM0TVr/c8w0ZA7Ipoder9K/9LdGpIgoc3qa8hdY2nH
|
|
||||||
TwGYJ53aQv32neOcg3p/vzqdzgmwbk4JLjcIMhOuTUj4xM8OMnkxRpyy9+Ghi22X
|
|
||||||
oZXDxu8meI2Pc8jM+zpRYb0wd06dd231m03CK80LAwSvIn7dGFAr+xTF5XKopXHY
|
|
||||||
vuT+9SshszbP4+pSqbEHZhJOX1/os+Uo8KKfysifJBKfKCVvVWho8QCWoXgiNuOJ
|
|
||||||
3cYoThfWwUpIS1S51el/fPPSk3K295jlZAON9yEszdzKHGVGOrtJ7e9XSxKIXqhG
|
|
||||||
us2XA14eMvhQdaOgd/bscXIYe4YzqvaqvVRiDUP8bzA+4w0ctB0w9HRFGK5lajTn
|
|
||||||
/QQvkKP9JQXm6Tb2GB+wjuU3wPXhKRWscEzbHVwMq2WiaYH5vWVhHI6lbqXcWkvZ
|
|
||||||
i2gZXQPyrAKzUau1Z2lBN2xi2cv5+9JJth5pHebuLOWbuf1WV4nR1fdSNdG7GGmj
|
|
||||||
G951w/1bTqIlzN4Vl6kdore4u45U4kO4Xf7Hq8b8k8ys107ENpgO7lB9KLoMMFKS
|
|
||||||
vjG+EPEF3g8ywKaS8mZQX+sCAwEAAQ==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
```
|
|
||||||
|
|
||||||
# piv-ecdh
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ card-cli piv-ecdh --public-256 --public-key-point-hex 04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0 --json
|
|
||||||
{
|
|
||||||
"epk_point_hex": "04bbb6a458e81d2c646587118abfb029ff715db366f92a1d0468887f9947f176c11961eccebd5b9cbbb8b67e33fa8d3f0010a4aaf5010d0f419f1f99b4c2d7aa56",
|
|
||||||
"pk_point_hex": "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0",
|
|
||||||
"shared_secret_hex": "58069f1b2ce85c4f2232070567bef99f71b45f69ab321c4c782e599813b56f25"
|
|
||||||
}
|
|
||||||
|
|
||||||
$ card-cli piv-ecdh --private --slot 82 --epk 04bbb6a458e81d2c646587118abfb029ff715db366f92a1d0468887f9947f176c11961eccebd5b9cbbb8b67e33fa8d3f0010a4aaf5010d0f419f1f99b4c2d7aa56 --json
|
|
||||||
[WARN ] Get slot: 82 meta data failed
|
|
||||||
{
|
|
||||||
"shared_secret_hex": "58069f1b2ce85c4f2232070567bef99f71b45f69ab321c4c782e599813b56f25"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# piv-ecsign
|
With those steps completed, you should be ready to start using Swift code from Rust!
|
||||||
|
|
||||||
```shell
|
If you experience the error `dyld[16008]: Library not loaded: @rpath/libswiftCore.dylib`
|
||||||
$ card-cli piv-ecsign -s 82 --hash-hex 8f25018489d6fe0dec34a352314c38dc146247b7de65735790f4398a92afa84b --json
|
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
|
||||||
"hash_hex": "8f25018489d6fe0dec34a352314c38dc146247b7de65735790f4398a92afa84b",
|
|
||||||
"signed_data_base64": "MEUCICdes5Y0Id7KBNL23ZsTXXXGAzmsWYyDa6szQwjCxhCJAiEAhJotD2dPK/fWNjNrwkrPd0F20MpGgIY3WiKDR7YgJbk=",
|
To allow calling a Swift function from Rust, it must follow some rules:
|
||||||
"signed_data_hex": "30450220275eb3963421deca04d2f6dd9b135d75c60339ac598c836bab334308c2c61089022100849a2d0f674f2bf7d636336bc24acf774176d0ca468086375a228347b62025b9",
|
|
||||||
"slot": "82"
|
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
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# import private key to PIV card & generate certificate
|
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`.
|
||||||
|
|
||||||
```shell
|
```swift
|
||||||
$ ykman piv keys import --pin-policy NEVER --touch-policy CACHED 82 private_key.pem
|
@_cdecl("square_number")
|
||||||
|
public func squareNumber(number: Int) -> Int {
|
||||||
|
return number * number
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Parameter | Description |
|
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:
|
||||||
| --pin-policy | \[ DEFAULT \| NEVER \| ONCE \| ALWAYS \] PIN policy for slot |
|
|
||||||
| --touch-policy | \[ DEFAULT \| NEVER \| ALWAYS \| CACHED \] touch policy for slot |
|
|
||||||
|
|
||||||
```shell
|
```rust
|
||||||
$ ykman piv certificates generate 82 public_key.pem -s 'O=age-plugin-yubikey,OU=0.3.3,CN=hatter-yk'
|
use swift_rs::swift;
|
||||||
|
|
||||||
|
swift!(fn square_number(number: Int) -> Int);
|
||||||
```
|
```
|
||||||
|
|
||||||
# age
|
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`.
|
||||||
|
|
||||||
## pgp-age-address
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let input: Int = 4;
|
||||||
|
let output = unsafe { square_number(input) };
|
||||||
|
|
||||||
```shell
|
println!("Input: {}, Squared: {}", input, output);
|
||||||
$ card-cli pgp-age-address
|
// Prints "Input: 4, Squared: 16"
|
||||||
[INFO ] Found 1 card(s)
|
}
|
||||||
[OK ] Found card #0: Ok(ApplicationIdentifier { application: 1, version: 772, manufacturer: 6, serial: 370378374 })
|
|
||||||
[OK ] Age address: age10l464vxcpnkjguctvylnmp5jg4swhncn4quda0qxta3ud8pycc0qeaj2te
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# sign-jwt
|
Check [the documentation](TODO) for all available helper types.
|
||||||
|
|
||||||
Sign a JWT:
|
## Returning objects from Swift
|
||||||
```shell
|
|
||||||
card-cli sign-jwt -s r3 \
|
Let's say that we want our `squareNumber` function to return not only the result, but also the original input.
|
||||||
-C iss:****** \
|
A standard way to do this in Swift would be with a struct:
|
||||||
-C sub:****** \
|
|
||||||
-C aud:client_gard****** \
|
```swift
|
||||||
-K KEY=ID \
|
struct SquareNumberResult {
|
||||||
--jti \
|
var input: Int
|
||||||
--validity 10m --json
|
var output: Int
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# SSH CA
|
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`:
|
||||||
|
|
||||||
## Generate SSH root CA
|
```swift
|
||||||
|
class SquareNumberResult: NSObject {
|
||||||
|
var input: Int
|
||||||
|
var output: Int
|
||||||
|
|
||||||
```shell
|
init(_ input: Int, _ output: Int) {
|
||||||
card-cli ssh-pub-key --ca -s r15
|
self.input = input;
|
||||||
|
self.output = output
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Outputs:
|
<sub><sup>Yes, this class could contain the squaring logic too, but that is irrelevant for this example
|
||||||
```
|
|
||||||
cert-authority,principals="root" ecdsa-sha2-nistp384 AAAAE2VjZHNh****** Yubikey-PIV-R15
|
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)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> `principals` can be multiple items, split by `,`, e.g. `root,hatterink`
|
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.
|
||||||
|
|
||||||
## Generate SSH user CA
|
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:
|
||||||
|
|
||||||
```shell
|
```rust
|
||||||
ssh-keygen -f id_user
|
use swift_rs::{swift, Int, SRObject};
|
||||||
|
|
||||||
card-cli ssh-piv-cert --pub id_user.pub -s r15
|
// 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>);
|
||||||
```
|
```
|
||||||
|
|
||||||
Show SSH CA cert details:
|
Then, using the new return value is just like using `SquareNumberResult` directly:
|
||||||
```shell
|
|
||||||
ssh-keygen -L -f id_user-cert.pub
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let input = 4;
|
||||||
|
let result = unsafe { square_number(input) };
|
||||||
|
|
||||||
|
let result_input = result.input; // 4
|
||||||
|
let result_output = result.output; // 16
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
SSH to server:
|
Creating objects in Rust and then passing them to Swift is not supported.
|
||||||
```shell
|
|
||||||
ssh -i id_user root@example.com
|
## 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!
|
||||||
|
|
||||||
<br><br>
|
```rust
|
||||||
|
use swift_rs::{swift, Bool, SRString};
|
||||||
|
|
||||||
Downloads:
|
swift!(optional_string(return_nil: Bool) -> Option<SRString>)
|
||||||
* https://developers.yubico.com/yubikey-manager/
|
```
|
||||||
|
|
||||||
<br>
|
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!
|
||||||
|
|
||||||
Related projects:
|
## Complex types
|
||||||
* https://crates.io/crates/openpgp-card-tools
|
|
||||||
* https://github.com/sekey/sekey
|
So far we have only looked at using primitive types and structs/classes,
|
||||||
* https://github.com/str4d/age-plugin-yubikey
|
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.
|
||||||
|
|||||||
9
build.rs
Normal file
9
build.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
25
src/cmd_se.rs
Normal file
25
src/cmd_se.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use crate::seutil;
|
||||||
|
use clap::{App, ArgMatches, SubCommand};
|
||||||
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
|
|
||||||
|
pub struct CommandImpl;
|
||||||
|
|
||||||
|
impl Command for CommandImpl {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"se"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subcommand<'a>(&self) -> App<'a, 'a> {
|
||||||
|
SubCommand::with_name(self.name()).about("Secure Enclave subcommand")
|
||||||
|
// .arg(Arg::with_name("json").long("json").help("JSON output"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError {
|
||||||
|
if seutil::is_support_se() {
|
||||||
|
success!("Secure Enclave is supported.")
|
||||||
|
} else {
|
||||||
|
failure!("Secure Enclave is NOT supported.")
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/cmd_se_generate.rs
Normal file
73
src/cmd_se_generate.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use crate::pkiutil::bytes_to_pem;
|
||||||
|
use crate::seutil;
|
||||||
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
|
use rust_util::util_msg;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
pub struct CommandImpl;
|
||||||
|
|
||||||
|
impl Command for CommandImpl {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"se-generate"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subcommand<'a>(&self) -> App<'a, 'a> {
|
||||||
|
SubCommand::with_name(self.name())
|
||||||
|
.about("Secure Enclave subcommand")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("type")
|
||||||
|
.long("type")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Type signing or key_agreement"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("host")
|
||||||
|
.long("host")
|
||||||
|
.required(false)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Host name"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("json").long("json").help("JSON output"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
|
||||||
|
if !seutil::is_support_se() {
|
||||||
|
return simple_error!("Secure Enclave is NOT supported.");
|
||||||
|
}
|
||||||
|
let ty = sub_arg_matches.value_of("type").unwrap();
|
||||||
|
let host = sub_arg_matches.value_of("host").unwrap_or("macbook");
|
||||||
|
let json_output = sub_arg_matches.is_present("json");
|
||||||
|
if json_output {
|
||||||
|
util_msg::set_logger_std_out(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sign = match ty {
|
||||||
|
"signing" | "ecsign" | "sign" => true,
|
||||||
|
"key_agreement" | "ecdh" | "dh" => false,
|
||||||
|
_ => return simple_error!("Invalie type: {}", ty),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (public_key_point, public_key_der, private_key) =
|
||||||
|
seutil::generate_secure_enclave_p256_keypair(sign)?;
|
||||||
|
|
||||||
|
let public_key_point_hex = hex::encode(&public_key_point);
|
||||||
|
let public_key_pem = bytes_to_pem("PUBLIC KEY", &*public_key_der);
|
||||||
|
let key = format!("key://{}:se/p256:{}:{}",
|
||||||
|
host, iff!(sign, "signing", "key_agreement"), private_key,
|
||||||
|
);
|
||||||
|
if json_output {
|
||||||
|
let mut json = BTreeMap::<&'_ str, String>::new();
|
||||||
|
json.insert("public_key_point", public_key_point_hex);
|
||||||
|
json.insert("public_key_pem", public_key_pem);
|
||||||
|
json.insert("key", key);
|
||||||
|
} else {
|
||||||
|
success!("Public key(point): {}", public_key_point_hex);
|
||||||
|
success!("Public key PEM: {}", public_key_pem);
|
||||||
|
success!("Key: {}", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
98
src/main.rs
98
src/main.rs
@@ -4,56 +4,62 @@ extern crate rust_util;
|
|||||||
use clap::{App, AppSettings, ArgMatches};
|
use clap::{App, AppSettings, ArgMatches};
|
||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
|
|
||||||
mod util;
|
|
||||||
mod sshutil;
|
|
||||||
mod fido;
|
|
||||||
mod digest;
|
|
||||||
mod pivutil;
|
|
||||||
mod rsautil;
|
|
||||||
mod pkiutil;
|
|
||||||
mod hmacutil;
|
|
||||||
mod ecdsautil;
|
|
||||||
mod argsutil;
|
mod argsutil;
|
||||||
mod pgpcardutil;
|
|
||||||
mod cmd_list;
|
|
||||||
mod cmd_u2fregister;
|
|
||||||
mod cmd_u2fsign;
|
|
||||||
mod cmd_rsaencrypt;
|
|
||||||
mod cmd_rsadecrypt;
|
|
||||||
mod cmd_rsaverify;
|
|
||||||
#[cfg(feature = "with-sequoia-openpgp")]
|
|
||||||
mod cmd_pgp;
|
|
||||||
mod cmd_pgpcardadmin;
|
|
||||||
mod cmd_pgpcardlist;
|
|
||||||
mod cmd_pgpcardsign;
|
|
||||||
mod cmd_pgpcarddecrypt;
|
|
||||||
#[cfg(feature = "with-sequoia-openpgp")]
|
|
||||||
mod cmd_pgpcardmake;
|
|
||||||
mod cmd_piv;
|
|
||||||
mod cmd_pivsummary;
|
|
||||||
mod cmd_pivmeta;
|
|
||||||
mod cmd_pivverify;
|
|
||||||
mod cmd_pivrsasign;
|
|
||||||
mod cmd_pivecdh;
|
|
||||||
mod cmd_pivecsign;
|
|
||||||
mod cmd_pivdecrypt;
|
|
||||||
mod cmd_pivgenerate;
|
|
||||||
mod cmd_hmac_sha1;
|
|
||||||
mod cmd_chall;
|
mod cmd_chall;
|
||||||
mod cmd_challconfig;
|
mod cmd_challconfig;
|
||||||
mod cmd_sshagent;
|
mod cmd_hmac_sha1;
|
||||||
mod cmd_sshparsesign;
|
mod cmd_list;
|
||||||
mod cmd_sshpivsign;
|
#[cfg(feature = "with-sequoia-openpgp")]
|
||||||
mod cmd_sshpivcert;
|
mod cmd_pgp;
|
||||||
mod cmd_sshpubkey;
|
|
||||||
mod cmd_sshparse;
|
|
||||||
mod cmd_pgpageaddress;
|
mod cmd_pgpageaddress;
|
||||||
mod cmd_signjwt;
|
mod cmd_pgpcardadmin;
|
||||||
|
mod cmd_pgpcarddecrypt;
|
||||||
|
mod cmd_pgpcardlist;
|
||||||
|
#[cfg(feature = "with-sequoia-openpgp")]
|
||||||
|
mod cmd_pgpcardmake;
|
||||||
|
mod cmd_pgpcardsign;
|
||||||
|
mod cmd_piv;
|
||||||
|
mod cmd_pivdecrypt;
|
||||||
|
mod cmd_pivecdh;
|
||||||
|
mod cmd_pivecsign;
|
||||||
|
mod cmd_pivgenerate;
|
||||||
|
mod cmd_pivmeta;
|
||||||
|
mod cmd_pivrsasign;
|
||||||
|
mod cmd_pivsummary;
|
||||||
|
mod cmd_pivverify;
|
||||||
|
mod cmd_rsadecrypt;
|
||||||
|
mod cmd_rsaencrypt;
|
||||||
|
mod cmd_rsaverify;
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
mod cmd_se;
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
mod cmd_se_generate;
|
||||||
mod cmd_signfile;
|
mod cmd_signfile;
|
||||||
|
mod cmd_signjwt;
|
||||||
|
mod cmd_sshagent;
|
||||||
|
mod cmd_sshparse;
|
||||||
|
mod cmd_sshparsesign;
|
||||||
|
mod cmd_sshpivcert;
|
||||||
|
mod cmd_sshpivsign;
|
||||||
|
mod cmd_sshpubkey;
|
||||||
|
mod cmd_u2fregister;
|
||||||
|
mod cmd_u2fsign;
|
||||||
mod cmd_verifyfile;
|
mod cmd_verifyfile;
|
||||||
mod signfile;
|
mod digest;
|
||||||
mod ecdhutil;
|
mod ecdhutil;
|
||||||
|
mod ecdsautil;
|
||||||
|
mod fido;
|
||||||
|
mod hmacutil;
|
||||||
|
mod pgpcardutil;
|
||||||
mod pinutil;
|
mod pinutil;
|
||||||
|
mod pivutil;
|
||||||
|
mod pkiutil;
|
||||||
|
mod rsautil;
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
mod seutil;
|
||||||
|
mod signfile;
|
||||||
|
mod sshutil;
|
||||||
|
mod util;
|
||||||
|
|
||||||
pub struct DefaultCommandImpl;
|
pub struct DefaultCommandImpl;
|
||||||
|
|
||||||
@@ -117,11 +123,17 @@ fn inner_main() -> CommandError {
|
|||||||
Box::new(cmd_signjwt::CommandImpl),
|
Box::new(cmd_signjwt::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),
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
Box::new(cmd_se_generate::CommandImpl),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut features: Vec<&str> = vec![];
|
let mut features: Vec<&str> = vec![];
|
||||||
#[cfg(feature = "with-sequoia-openpgp")]
|
#[cfg(feature = "with-sequoia-openpgp")]
|
||||||
features.push("with-sequoia-openpgp");
|
features.push("sequoia-openpgp");
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
features.push("secure-enclave");
|
||||||
let about = format!(
|
let about = format!(
|
||||||
"{}, features: [{}]",
|
"{}, features: [{}]",
|
||||||
"Card Cli is a command tool for WebAuthn, OpenPGP, YubiKey ... smart cards",
|
"Card Cli is a command tool for WebAuthn, OpenPGP, YubiKey ... smart cards",
|
||||||
|
|||||||
46
src/seutil.rs
Normal file
46
src/seutil.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use crate::util::base64_decode;
|
||||||
|
use rust_util::XResult;
|
||||||
|
use swift_rs::swift;
|
||||||
|
use swift_rs::{Bool, SRString};
|
||||||
|
|
||||||
|
swift!(fn is_support_secure_enclave() -> Bool);
|
||||||
|
swift!(fn generate_secure_enclave_p256_ecdh_keypair() -> SRString);
|
||||||
|
swift!(fn generate_secure_enclave_p256_ecsign_keypair() -> SRString);
|
||||||
|
swift!(fn compute_secure_enclave_p256_ecdh(private_key_base64: SRString, ephemera_public_key_base64: SRString) -> SRString);
|
||||||
|
|
||||||
|
pub fn is_support_se() -> bool {
|
||||||
|
unsafe { is_support_secure_enclave() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_secure_enclave_p256_keypair(sign: bool) -> XResult<(Vec<u8>, Vec<u8>, String)> {
|
||||||
|
let p256_keypair_result = if sign {
|
||||||
|
unsafe { generate_secure_enclave_p256_ecsign_keypair() }
|
||||||
|
} else {
|
||||||
|
unsafe { generate_secure_enclave_p256_ecdh_keypair() }
|
||||||
|
};
|
||||||
|
let p256_keypair_result_str = p256_keypair_result.as_str();
|
||||||
|
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))
|
||||||
|
}
|
||||||
30
swift-lib/Package.swift
Normal file
30
swift-lib/Package.swift
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// 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")
|
||||||
|
]
|
||||||
|
)
|
||||||
84
swift-lib/src/lib.swift
Normal file
84
swift-lib/src/lib.swift
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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("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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
417
swift-rs/Cargo.lock
generated
Normal file
417
swift-rs/Cargo.lock
generated
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
# 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"
|
||||||
34
swift-rs/Cargo.toml
Normal file
34
swift-rs/Cargo.toml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[package]
|
||||||
|
name = "swift-rs"
|
||||||
|
version = "1.0.6"
|
||||||
|
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"]
|
||||||
201
swift-rs/LICENSE-APACHE
Normal file
201
swift-rs/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
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.
|
||||||
19
swift-rs/LICENSE-MIT
Normal file
19
swift-rs/LICENSE-MIT
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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.
|
||||||
30
swift-rs/Package.swift
Normal file
30
swift-rs/Package.swift
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// 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")
|
||||||
|
]
|
||||||
|
)
|
||||||
483
swift-rs/README.md
Normal file
483
swift-rs/README.md
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
# 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.
|
||||||
26
swift-rs/src-rs/autorelease.rs
Normal file
26
swift-rs/src-rs/autorelease.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/// 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
|
||||||
|
}};
|
||||||
|
}
|
||||||
326
swift-rs/src-rs/build.rs
Normal file
326
swift-rs/src-rs/build.rs
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
#![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");
|
||||||
|
}
|
||||||
90
swift-rs/src-rs/dark_magic.rs
Normal file
90
swift-rs/src-rs/dark_magic.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/// 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)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
20
swift-rs/src-rs/lib.rs
Normal file
20
swift-rs/src-rs/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//! 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::*;
|
||||||
101
swift-rs/src-rs/swift.rs
Normal file
101
swift-rs/src-rs/swift.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
75
swift-rs/src-rs/swift_arg.rs
Normal file
75
swift-rs/src-rs/swift_arg.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
55
swift-rs/src-rs/swift_ret.rs
Normal file
55
swift-rs/src-rs/swift_ret.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
20
swift-rs/src-rs/test-build.rs
Normal file
20
swift-rs/src-rs/test-build.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//! 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
110
swift-rs/src-rs/types/array.rs
Normal file
110
swift-rs/src-rs/types/array.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
75
swift-rs/src-rs/types/data.rs
Normal file
75
swift-rs/src-rs/types/data.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
swift-rs/src-rs/types/mod.rs
Normal file
11
swift-rs/src-rs/types/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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::*;
|
||||||
75
swift-rs/src-rs/types/object.rs
Normal file
75
swift-rs/src-rs/types/object.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
34
swift-rs/src-rs/types/scalars.rs
Normal file
34
swift-rs/src-rs/types/scalars.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/// 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;
|
||||||
84
swift-rs/src-rs/types/string.rs
Normal file
84
swift-rs/src-rs/types/string.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
94
swift-rs/src-swift/lib.swift
Normal file
94
swift-rs/src-swift/lib.swift
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
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