diff --git a/README.md b/README.md index 2e83a2d..1fcb2ed 100644 --- a/README.md +++ b/README.md @@ -1,483 +1,238 @@ -# swift-rs +# card-cli -![Crates.io](https://img.shields.io/crates/v/swift-rs?color=blue&style=flat-square) -![docs.rs](https://img.shields.io/docsrs/swift-rs?color=blue&style=flat-square) +> FIDO(U2F, WebAuthn), YubiKey, OpenPGP command line tool -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"] } +Install: +```shell +cargo install --git https://git.hatter.ink/hatter/card-cli.git ``` -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" - ) - ], - ) - ] -) +Compile without features: +```shell +cargo build --release --no-default-features ``` -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; +# PGP -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(); +## encrypt & decrypt - // Other build steps +sample encrypt 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----- +``` + +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" } ``` -With those steps completed, you should be ready to start using Swift code from Rust! +# piv-ecsign -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`. +```shell +$ card-cli piv-ecsign -s 82 --hash-hex 8f25018489d6fe0dec34a352314c38dc146247b7de65735790f4398a92afa84b --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 +{ + "hash_hex": "8f25018489d6fe0dec34a352314c38dc146247b7de65735790f4398a92afa84b", + "signed_data_base64": "MEUCICdes5Y0Id7KBNL23ZsTXXXGAzmsWYyDa6szQwjCxhCJAiEAhJotD2dPK/fWNjNrwkrPd0F20MpGgIY3WiKDR7YgJbk=", + "signed_data_hex": "30450220275eb3963421deca04d2f6dd9b135d75c60339ac598c836bab334308c2c61089022100849a2d0f674f2bf7d636336bc24acf774176d0ca468086375a228347b62025b9", + "slot": "82" } ``` -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`. +# import private key to PIV card & generate certificate -```swift -@_cdecl("square_number") -public func squareNumber(number: Int) -> Int { - return number * number -} +```shell +$ ykman piv keys import --pin-policy NEVER --touch-policy CACHED 82 private_key.pem ``` -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: +| Parameter | Description | +| ---- |------------------------------------------------------------------| +| --pin-policy | \[ DEFAULT \| NEVER \| ONCE \| ALWAYS \] PIN policy for slot | +| --touch-policy | \[ DEFAULT \| NEVER \| ALWAYS \| CACHED \] touch policy for slot | -```rust -use swift_rs::swift; - -swift!(fn square_number(number: Int) -> Int); +```shell +$ ykman piv certificates generate 82 public_key.pem -s 'O=age-plugin-yubikey,OU=0.3.3,CN=hatter-yk' ``` -Lastly, you can call the function from regular Rust functions. -Note that all calls to a Swift function are unsafe, -and require wrapping in an `unsafe {}` block or `unsafe fn`. +# age -```rust -fn main() { - let input: Int = 4; - let output = unsafe { square_number(input) }; +## pgp-age-address - println!("Input: {}, Squared: {}", input, output); - // Prints "Input: 4, Squared: 16" -} +```shell +$ card-cli pgp-age-address +[INFO ] Found 1 card(s) +[OK ] Found card #0: Ok(ApplicationIdentifier { application: 1, version: 772, manufacturer: 6, serial: 370378374 }) +[OK ] Age address: age10l464vxcpnkjguctvylnmp5jg4swhncn4quda0qxta3ud8pycc0qeaj2te ``` -Check [the documentation](TODO) for all available helper types. +# sign-jwt -## 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 -} +Sign a JWT: +```shell +card-cli sign-jwt -s r3 \ + -C iss:****** \ + -C sub:****** \ + -C aud:client_gard****** \ + -K KEY=ID \ + --jti \ + --validity 10m --json ``` -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`: +# SSH CA -```swift -class SquareNumberResult: NSObject { - var input: Int - var output: Int +## Generate SSH root CA - init(_ input: Int, _ output: Int) { - self.input = input; - self.output = output - } -} +```shell +card-cli ssh-pub-key --ca -s r15 ``` -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) -} +Outputs: +``` +cert-authority,principals="root" ecdsa-sha2-nistp384 AAAAE2VjZHNh****** Yubikey-PIV-R15 ``` -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. +> `principals` can be multiple items, split by `,`, e.g. `root,hatterink` -This may sound daunting, but it's not actually a problem thanks to `SRObject`. -This type manages the pointer internally, and takes a generic argument for a struct that we can access the data through. -Let's see how we'd implement `SquareNumberResult` in Rust: +## Generate SSH user CA -```rust -use swift_rs::{swift, Int, SRObject}; +```shell +ssh-keygen -f id_user -// 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); +card-cli ssh-piv-cert --pub id_user.pub -s r15 ``` -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 -} +Show SSH CA cert details: +```shell +ssh-keygen -L -f id_user-cert.pub ``` -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") -} +SSH to server: +```shell +ssh -i id_user root@example.com ``` -Thanks to Rust's [null pointer optimisation](https://doc.rust-lang.org/std/option/index.html#representation), -the optional nature of `SRString?` can be represented by wrapping `SRString` in Rust's `Option` type! -```rust -use swift_rs::{swift, Bool, SRString}; +

-swift!(optional_string(return_nil: Bool) -> Option) -``` +Downloads: +* https://developers.yubico.com/yubikey-manager/ -Null pointers are actually the reason why a function that returns an optional primitive cannot be represented in C. -If this were to be supported, how could a `nil` be differentiated from a number? It can't! +
-## Complex types - -So far we have only looked at using primitive types and structs/classes, -but this leaves out some of the most important data structures: arrays (`SRArray`) and strings (`SRString`). -These types must be treated with caution, however, and are not as flexible as their native Swift & Rust counterparts. - -### Strings - -Strings can be passed between Rust and Swift through `SRString`, which can be created from native strings in either language. - -**As an argument** - -```swift -import SwiftRs - -@_cdecl("swift_print") -public func swiftPrint(value: SRString) { - // .to_string() converts the SRString to a Swift String - print(value.to_string()) -} -``` - -```rust -use swift_rs::{swift, SRString, SwiftRef}; - -swift!(fn swift_print(value: &SRString)); - -fn main() { - // SRString can be created by simply calling .into() on any string reference. - // This will allocate memory in Swift and copy the string - let value: SRString = "lorem ipsum".into(); - - unsafe { swift_print(&value) }; // Will print "lorem ipsum" to the console -} -``` - -**As a return value** - -```swift -import SwiftRs - -@_cdecl("get_string") -public func getString() -> SRString { - let value = "lorem ipsum" - - // SRString can be created from a regular String - return SRString(value) -} -``` - -```rust -use swift_rs::{swift, SRString}; - -swift!(fn get_string() -> SRString); - -fn main() { - let value_srstring = unsafe { get_string() }; - - // SRString can be converted to an &str using as_str()... - let value_str: &str = value_srstring.as_str(); - // or though the Deref trait - let value_str: &str = &*value_srstring; - - // SRString also implements Display - println!("{}", value_srstring); // Will print "lorem ipsum" to the console -} -``` - -### Arrays - -**Primitive Arrays** - -Representing arrays properly is tricky, since we cannot use generics as Swift arguments or return values according to rule 3. -Instead, `swift-rs` provides a generic `SRArray` that can be embedded inside another class that extends `NSObject` that is not generic, -but is restricted to a single element type. - -```swift -import SwiftRs - -// Argument/Return values can contain generic types, but cannot be generic themselves. -// This includes extending generic types. -class IntArray: NSObject { - var data: SRArray - - init(_ data: [Int]) { - self.data = SRArray(data) - } -} - -@_cdecl("get_numbers") -public func getNumbers() -> IntArray { - let numbers = [1, 2, 3, 4] - - return IntArray(numbers) -} -``` - -```rust -use swift_rs::{Int, SRArray, SRObject}; - -#[repr(C)] -struct IntArray { - data: SRArray -} - -// Since IntArray extends NSObject in its Swift implementation, -// it must be wrapped in SRObject on the Rust side -swift!(fn get_numbers() -> SRObject); - -fn main() { - let numbers = unsafe { get_numbers() }; - - // SRArray can be accessed as a slice via as_slice - let numbers_slice: &[Int] = numbers.data.as_slice(); - - assert_eq!(numbers_slice, &[1, 2, 3, 4]); -} -``` - -To simplify things on the rust side, we can actually do away with the `IntArray` struct. -Since `IntArray` only has one field, its memory layout is identical to that of `SRArray`, -so our Rust implementation can be simplified at the cost of equivalence with our Swift code: - -```rust -// We still need to wrap the array in SRObject since -// the wrapper class in Swift is an NSObject -swift!(fn get_numbers() -> SRObject>); -``` - -**NSObject Arrays** - -What if we want to return an `NSObject` array? There are two options on the Swift side: - -1. Continue using `SRArray` and a custom wrapper type, or -2. Use `SRObjectArray`, a wrapper type provided by `swift-rs` that accepts any `NSObject` as its elements. -This can be easier than continuing to create wrapper types, but sacrifices some type safety. - -There is also `SRObjectArray` for Rust, which is compatible with any single-element Swift wrapper type (and of course `SRObjectArray` in Swift), -and automatically wraps its elements in `SRObject`, so there's very little reason to not use it unless you _really_ like custom wrapper types. - -Using `SRObjectArray` in both Swift and Rust with a basic custom class/struct can be done like this: - -```swift -import SwiftRs - -class IntTuple: NSObject { - var item1: Int - var item2: Int - - init(_ item1: Int, _ item2: Int) { - self.item1 = item1 - self.item2 = item2 - } -} - -@_cdecl("get_tuples") -public func getTuples() -> SRObjectArray { - let tuple1 = IntTuple(0,1), - tuple2 = IntTuple(2,3), - tuple3 = IntTuple(4,5) - - let tupleArray: [IntTuple] = [ - tuple1, - tuple2, - tuple3 - ] - - // Type safety is only lost when the Swift array is converted to an SRObjectArray - return SRObjectArray(tupleArray) -} -``` - -```rust -use swift_rs::{swift, Int, SRObjectArray}; - -#[repr(C)] -struct IntTuple { - item1: Int, - item2: Int -} - -// No need to wrap IntTuple in SRObject since -// SRObjectArray does it automatically -swift!(fn get_tuples() -> SRObjectArray); - -fn main() { - let tuples = unsafe { get_tuples() }; - - for tuple in tuples.as_slice() { - // Will print each tuple's contents to the console - println!("Item 1: {}, Item 2: {}", tuple.item1, tuple.item2); - } -} -``` - -Complex types can contain whatever combination of primitives and `SRObject` you like, just remember to follow the 3 rules! - -## Bonuses - -### SRData - -A wrapper type for `SRArray` designed for storing `u8`s - essentially just a byte buffer. - -### Tighter Memory Control with `autoreleasepool!` - -If you've come to Swift from an Objective-C background, you likely know the utility of `@autoreleasepool` blocks. -`swift-rs` has your back on this too, just wrap your block of code with a `autoreleasepool!`, and that block of code now executes with its own autorelease pool! - -```rust -use swift_rs::autoreleasepool; - -for _ in 0..10000 { - autoreleasepool!({ - // do some memory intensive thing here - }); -} -``` - -## Limitations - -Currently, the only types that can be created from Rust are number types, boolean, `SRString`, and `SRData`. -This is because those types are easy to allocate memory for, either on the stack or on the heap via calling out to swift, -whereas other types are not. This may be implemented in the future, though. - -Mutating values across Swift and Rust is not currently an aim for this library, it is purely for providing arguments and returning values. -Besides, this would go against Rust's programming model, potentially allowing for multiple shared references to a value instead of interior mutability via something like a Mutex. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. +Related projects: +* https://crates.io/crates/openpgp-card-tools +* https://github.com/sekey/sekey +* https://github.com/str4d/age-plugin-yubikey \ No newline at end of file