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
-
-
+> 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