test: async fix

Updates async functions in lib.rs.
Updates example app.
Update examples in README.md.
Add sign-up/in/out examples to README.md.
Bumps version to 0.7.1
This commit is contained in:
coreyphillips
2024-09-30 20:07:19 -04:00
parent 88def98eca
commit 64a4cc5113
13 changed files with 581 additions and 467 deletions

View File

@@ -16,15 +16,17 @@ npm install @synonymdev/react-native-pubky
- [x] [resolve](#resolve): Functionality to resolve content. - [x] [resolve](#resolve): Functionality to resolve content.
- [x] [publishHttps](#publishHttps): Publish HTTPS records. - [x] [publishHttps](#publishHttps): Publish HTTPS records.
- [x] [resolveHttps](#resolveHttps): Resolve HTTPS records. - [x] [resolveHttps](#resolveHttps): Resolve HTTPS records.
- [x] [signUp](#signUp): Sign-up to a homeserver and update Pkarr accordingly.
- [x] [signIn](#signIn): Sign-in to a homeserver.
- [x] [signOut](#signOut): Sign-out from a homeserver.
- [x] [put](#put): Upload a small payload to a given path. - [x] [put](#put): Upload a small payload to a given path.
- [x] [get](#get): Download a small payload from a given path relative to a pubky author. - [x] [get](#get): Download a small payload from a given path relative to a pubky author.
- [x] [list](#list): Returns a list of Pubky URLs of the files in the path of the `url` provided. - [x] [list](#list): Returns a list of Pubky URLs of the files in the path of the `url` provided.
- [x] [generateSecretKey](#generateSecretKey): Generate a secret key. - [x] [generateSecretKey](#generateSecretKey): Generate a secret key.
- [x] [getPublicKeyFromSecretKey](#getPublicKeyFromSecretKey): Get the public key string and uri from a secret key. - [x] [getPublicKeyFromSecretKey](#getPublicKeyFromSecretKey): Get the public key string and uri from a secret key.
### Methods to be Implemented ### Methods to be Implemented
- [ ] signIn: Sign-in to a homeserver. - [ ] getProfile: Retrieve the profile of a user.
- [ ] signUp: Sign-up to a homeserver and update Pkarr accordingly. - [ ] editProfile: Submit changes to the specified profile.
- [ ] signOut: Sign-out from a homeserver.
## Usage ## Usage
@@ -118,8 +120,8 @@ console.log(resolveHttpsRes.value);
import { put } from '@synonymdev/react-native-pubky'; import { put } from '@synonymdev/react-native-pubky';
const putRes = await put( const putRes = await put(
'url', // URL 'pubky://z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty/pub/synonym.to', // URL
'content', // Content { data: 'test content' }, // Content
); );
if (putRes.isErr()) { if (putRes.isErr()) {
console.log(putRes.error.message); console.log(putRes.error.message);
@@ -133,7 +135,7 @@ console.log(putRes.value);
import { get } from '@synonymdev/react-native-pubky'; import { get } from '@synonymdev/react-native-pubky';
const getRes = await get( const getRes = await get(
'url' // URL 'pubky://z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty/pub/synonym.to' // URL
); );
if (getRes.isErr()) { if (getRes.isErr()) {
console.log(getRes.error.message); console.log(getRes.error.message);
@@ -147,7 +149,7 @@ console.log(getRes.value);
import { list } from '@synonymdev/react-native-pubky'; import { list } from '@synonymdev/react-native-pubky';
const listRes = await list( const listRes = await list(
'url' // URL 'pubky://z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty/pub/' // URL
); );
if (listRes.isErr()) { if (listRes.isErr()) {
console.log(listRes.error.message); console.log(listRes.error.message);
@@ -180,6 +182,49 @@ if (getPublicKeyFromSecretKeyRes.isErr()) {
console.log(getPublicKeyFromSecretKeyRes.value); console.log(getPublicKeyFromSecretKeyRes.value);
``` ```
### <a name="signUp"></a>signUp
```js
import { signUp } from '@synonymdev/react-native-pubky';
const signUpRes = await signUp(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', // Secret
'pubky://8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo', // Homeserver
);
if (signUpRes.isErr()) {
console.log(signUpRes.error.message);
return;
}
console.log(signUpRes.value);
```
### <a name="signIn"></a>signIn
```js
import { signIn } from '@synonymdev/react-native-pubky';
const signInRes = await signIn(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key
);
if (signInRes.isErr()) {
console.log(signInRes.error.message);
return;
}
console.log(signInRes.value);
```
### <a name="signOut"></a>signIn
```js
import { signOut } from '@synonymdev/react-native-pubky';
const signOutRes = await signOut(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key
);
if (signOutRes.isErr()) {
console.log(signOutRes.error.message);
return;
}
console.log(signOutRes.value);
```
## Local Installation ## Local Installation
1. Clone & npm install: 1. Clone & npm install:

View File

@@ -1237,7 +1237,7 @@ PODS:
- ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- Yoga - Yoga
- react-native-pubky (0.7.0): - react-native-pubky (0.7.1):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
@@ -1757,7 +1757,7 @@ SPEC CHECKSUMS:
React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404 React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404
React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4 React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4
React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e
react-native-pubky: e296eaeb8422b0864f5807592eac144d7d9a6eae react-native-pubky: 1740252f1e510886c4239242d0b731bc4d96b91b
React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9 React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9
React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27 React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27
React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b

View File

@@ -16,6 +16,11 @@ import {
getPublicKeyFromSecretKey, getPublicKeyFromSecretKey,
} from '@synonymdev/react-native-pubky'; } from '@synonymdev/react-native-pubky';
const HOMESERVER = '8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo';
const SECRET_KEY =
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
const PUBLIC_KEY = 'z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty';
export default function App() { export default function App() {
return ( return (
<View style={styles.container}> <View style={styles.container}>
@@ -25,7 +30,7 @@ export default function App() {
try { try {
const res = await auth( const res = await auth(
'pubkyauth:///?caps=/pub/pubky.app/:rw,/pub/foo.bar/file:r&secret=U55XnoH6vsMCpx1pxHtt8fReVg4Brvu9C0gUBuw-Jkw&relay=http://167.86.102.121:4173/', 'pubkyauth:///?caps=/pub/pubky.app/:rw,/pub/foo.bar/file:r&secret=U55XnoH6vsMCpx1pxHtt8fReVg4Brvu9C0gUBuw-Jkw&relay=http://167.86.102.121:4173/',
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' SECRET_KEY
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -61,7 +66,7 @@ export default function App() {
const res = await publish( const res = await publish(
'recordnametest', // Record Name 'recordnametest', // Record Name
'recordcontenttest', // Record Content 'recordcontenttest', // Record Content
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key SECRET_KEY // Secret Key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -78,7 +83,7 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await resolve( const res = await resolve(
'z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty' // Public key PUBLIC_KEY // Public key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -95,8 +100,8 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await signUp( const res = await signUp(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', // Secret Key SECRET_KEY, // Secret Key
'pubky://8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo' // Homeserver `pubky://${HOMESERVER}` // Homeserver
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -113,7 +118,7 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await signIn( const res = await signIn(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key SECRET_KEY // Secret Key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -130,7 +135,7 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await signOut( const res = await signOut(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key SECRET_KEY // Secret Key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -146,7 +151,9 @@ export default function App() {
title={'put'} title={'put'}
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await put('', { data: 'test data' }); const res = await put(`pubky://${PUBLIC_KEY}/pub/synonym.to`, {
data: 'test data',
});
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
return; return;
@@ -161,7 +168,7 @@ export default function App() {
title={'get'} title={'get'}
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await get(''); const res = await get(`pubky://${PUBLIC_KEY}/pub/synonym.to`);
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
return; return;
@@ -180,7 +187,7 @@ export default function App() {
const res = await publishHttps( const res = await publishHttps(
'example.com', // Record Name 'example.com', // Record Name
'target.example.com', // Target 'target.example.com', // Target
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key SECRET_KEY // Secret Key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -198,7 +205,7 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await resolveHttps( const res = await resolveHttps(
'z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty' // Public key PUBLIC_KEY // Public key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -216,7 +223,7 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await list( const res = await list(
'url' // URL `pubky://${PUBLIC_KEY}/pub/synonym.to` // URL
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);
@@ -249,7 +256,7 @@ export default function App() {
onPress={async (): Promise<void> => { onPress={async (): Promise<void> => {
try { try {
const res = await getPublicKeyFromSecretKey( const res = await getPublicKeyFromSecretKey(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // Secret Key SECRET_KEY // Secret Key
); );
if (res.isErr()) { if (res.isErr()) {
console.log(res.error.message); console.log(res.error.message);

View File

@@ -4,22 +4,6 @@
<dict> <dict>
<key>AvailableLibraries</key> <key>AvailableLibraries</key>
<array> <array>
<dict>
<key>BinaryPath</key>
<string>libpubkymobile.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libpubkymobile.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>libpubkymobile.a</string> <string>libpubkymobile.a</string>
@@ -38,6 +22,22 @@
<key>SupportedPlatformVariant</key> <key>SupportedPlatformVariant</key>
<string>simulator</string> <string>simulator</string>
</dict> </dict>
<dict>
<key>BinaryPath</key>
<string>libpubkymobile.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libpubkymobile.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XFWK</string> <string>XFWK</string>

View File

@@ -68,7 +68,7 @@ RustBuffer uniffi_pubkymobile_fn_func_auth(RustBuffer url, RustBuffer secret_key
RustBuffer uniffi_pubkymobile_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_get(RustBuffer url RustBuffer uniffi_pubkymobile_fn_func_get(RustBuffer url, RustCallStatus *_Nonnull out_status
); );
RustBuffer uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(RustBuffer secret_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
@@ -80,17 +80,17 @@ RustBuffer uniffi_pubkymobile_fn_func_publish(RustBuffer record_name, RustBuffer
); );
RustBuffer uniffi_pubkymobile_fn_func_publish_https(RustBuffer record_name, RustBuffer target, RustBuffer secret_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_publish_https(RustBuffer record_name, RustBuffer target, RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_put(RustBuffer url, RustBuffer content RustBuffer uniffi_pubkymobile_fn_func_put(RustBuffer url, RustBuffer content, RustCallStatus *_Nonnull out_status
); );
RustBuffer uniffi_pubkymobile_fn_func_resolve(RustBuffer public_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_resolve(RustBuffer public_key, RustCallStatus *_Nonnull out_status
); );
RustBuffer uniffi_pubkymobile_fn_func_resolve_https(RustBuffer public_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_resolve_https(RustBuffer public_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_sign_in(RustBuffer secret_key RustBuffer uniffi_pubkymobile_fn_func_sign_in(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_sign_out(RustBuffer secret_key RustBuffer uniffi_pubkymobile_fn_func_sign_out(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_sign_up(RustBuffer secret_key, RustBuffer homeserver RustBuffer uniffi_pubkymobile_fn_func_sign_up(RustBuffer secret_key, RustBuffer homeserver, RustCallStatus *_Nonnull out_status
); );
RustBuffer ffi_pubkymobile_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status RustBuffer ffi_pubkymobile_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status
); );

View File

@@ -68,7 +68,7 @@ RustBuffer uniffi_pubkymobile_fn_func_auth(RustBuffer url, RustBuffer secret_key
RustBuffer uniffi_pubkymobile_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_get(RustBuffer url RustBuffer uniffi_pubkymobile_fn_func_get(RustBuffer url, RustCallStatus *_Nonnull out_status
); );
RustBuffer uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(RustBuffer secret_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
@@ -80,17 +80,17 @@ RustBuffer uniffi_pubkymobile_fn_func_publish(RustBuffer record_name, RustBuffer
); );
RustBuffer uniffi_pubkymobile_fn_func_publish_https(RustBuffer record_name, RustBuffer target, RustBuffer secret_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_publish_https(RustBuffer record_name, RustBuffer target, RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_put(RustBuffer url, RustBuffer content RustBuffer uniffi_pubkymobile_fn_func_put(RustBuffer url, RustBuffer content, RustCallStatus *_Nonnull out_status
); );
RustBuffer uniffi_pubkymobile_fn_func_resolve(RustBuffer public_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_resolve(RustBuffer public_key, RustCallStatus *_Nonnull out_status
); );
RustBuffer uniffi_pubkymobile_fn_func_resolve_https(RustBuffer public_key, RustCallStatus *_Nonnull out_status RustBuffer uniffi_pubkymobile_fn_func_resolve_https(RustBuffer public_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_sign_in(RustBuffer secret_key RustBuffer uniffi_pubkymobile_fn_func_sign_in(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_sign_out(RustBuffer secret_key RustBuffer uniffi_pubkymobile_fn_func_sign_out(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
); );
void* _Nonnull uniffi_pubkymobile_fn_func_sign_up(RustBuffer secret_key, RustBuffer homeserver RustBuffer uniffi_pubkymobile_fn_func_sign_up(RustBuffer secret_key, RustBuffer homeserver, RustCallStatus *_Nonnull out_status
); );
RustBuffer ffi_pubkymobile_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status RustBuffer ffi_pubkymobile_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status
); );

View File

@@ -356,68 +356,6 @@ fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer {
return seq return seq
} }
} }
private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0
private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1
fileprivate func uniffiRustCallAsync<F, T>(
rustFutureFunc: () -> UnsafeMutableRawPointer,
pollFunc: (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> (),
completeFunc: (UnsafeMutableRawPointer, UnsafeMutablePointer<RustCallStatus>) -> F,
freeFunc: (UnsafeMutableRawPointer) -> (),
liftFunc: (F) throws -> T,
errorHandler: ((RustBuffer) throws -> Error)?
) async throws -> T {
// Make sure to call uniffiEnsureInitialized() since future creation doesn't have a
// RustCallStatus param, so doesn't use makeRustCall()
uniffiEnsureInitialized()
let rustFuture = rustFutureFunc()
defer {
freeFunc(rustFuture)
}
var pollResult: Int8;
repeat {
pollResult = await withUnsafeContinuation {
pollFunc(rustFuture, ContinuationHolder($0).toOpaque())
}
} while pollResult != UNIFFI_RUST_FUTURE_POLL_READY
return try liftFunc(makeRustCall(
{ completeFunc(rustFuture, $0) },
errorHandler: errorHandler
))
}
// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They
// lift the return value or error and resume the suspended function.
fileprivate func uniffiFutureContinuationCallback(ptr: UnsafeMutableRawPointer, pollResult: Int8) {
ContinuationHolder.fromOpaque(ptr).resume(pollResult)
}
// Wraps UnsafeContinuation in a class so that we can use reference counting when passing it across
// the FFI
fileprivate class ContinuationHolder {
let continuation: UnsafeContinuation<Int8, Never>
init(_ continuation: UnsafeContinuation<Int8, Never>) {
self.continuation = continuation
}
func resume(_ pollResult: Int8) {
self.continuation.resume(returning: pollResult)
}
func toOpaque() -> UnsafeMutableRawPointer {
return Unmanaged<ContinuationHolder>.passRetained(self).toOpaque()
}
static func fromOpaque(_ ptr: UnsafeRawPointer) -> ContinuationHolder {
return Unmanaged<ContinuationHolder>.fromOpaque(ptr).takeRetainedValue()
}
}
fileprivate func uniffiInitContinuationCallback() {
ffi_pubkymobile_rust_future_continuation_callback_set(uniffiFutureContinuationCallback)
}
public func auth(url: String, secretKey: String) -> [String] { public func auth(url: String, secretKey: String) -> [String] {
return try! FfiConverterSequenceString.lift( return try! FfiConverterSequenceString.lift(
@@ -437,24 +375,15 @@ public func generateSecretKey() -> [String] {
) )
} }
public func get(url: String) async -> [String] { public func get(url: String) -> [String] {
return try! await uniffiRustCallAsync( return try! FfiConverterSequenceString.lift(
rustFutureFunc: { try! rustCall() {
uniffi_pubkymobile_fn_func_get( uniffi_pubkymobile_fn_func_get(
FfiConverterString.lower(url) FfiConverterString.lower(url),$0)
) }
},
pollFunc: ffi_pubkymobile_rust_future_poll_rust_buffer,
completeFunc: ffi_pubkymobile_rust_future_complete_rust_buffer,
freeFunc: ffi_pubkymobile_rust_future_free_rust_buffer,
liftFunc: FfiConverterSequenceString.lift,
errorHandler: nil
) )
} }
public func getPublicKeyFromSecretKey(secretKey: String) -> [String] { public func getPublicKeyFromSecretKey(secretKey: String) -> [String] {
return try! FfiConverterSequenceString.lift( return try! FfiConverterSequenceString.lift(
try! rustCall() { try! rustCall() {
@@ -504,25 +433,16 @@ public func publishHttps(recordName: String, target: String, secretKey: String)
) )
} }
public func put(url: String, content: String) async -> [String] { public func put(url: String, content: String) -> [String] {
return try! await uniffiRustCallAsync( return try! FfiConverterSequenceString.lift(
rustFutureFunc: { try! rustCall() {
uniffi_pubkymobile_fn_func_put( uniffi_pubkymobile_fn_func_put(
FfiConverterString.lower(url), FfiConverterString.lower(url),
FfiConverterString.lower(content) FfiConverterString.lower(content),$0)
) }
},
pollFunc: ffi_pubkymobile_rust_future_poll_rust_buffer,
completeFunc: ffi_pubkymobile_rust_future_complete_rust_buffer,
freeFunc: ffi_pubkymobile_rust_future_free_rust_buffer,
liftFunc: FfiConverterSequenceString.lift,
errorHandler: nil
) )
} }
public func resolve(publicKey: String) -> [String] { public func resolve(publicKey: String) -> [String] {
return try! FfiConverterSequenceString.lift( return try! FfiConverterSequenceString.lift(
try! rustCall() { try! rustCall() {
@@ -541,61 +461,34 @@ public func resolveHttps(publicKey: String) -> [String] {
) )
} }
public func signIn(secretKey: String) async -> [String] { public func signIn(secretKey: String) -> [String] {
return try! await uniffiRustCallAsync( return try! FfiConverterSequenceString.lift(
rustFutureFunc: { try! rustCall() {
uniffi_pubkymobile_fn_func_sign_in( uniffi_pubkymobile_fn_func_sign_in(
FfiConverterString.lower(secretKey) FfiConverterString.lower(secretKey),$0)
) }
},
pollFunc: ffi_pubkymobile_rust_future_poll_rust_buffer,
completeFunc: ffi_pubkymobile_rust_future_complete_rust_buffer,
freeFunc: ffi_pubkymobile_rust_future_free_rust_buffer,
liftFunc: FfiConverterSequenceString.lift,
errorHandler: nil
) )
} }
public func signOut(secretKey: String) -> [String] {
return try! FfiConverterSequenceString.lift(
public func signOut(secretKey: String) async -> [String] { try! rustCall() {
return try! await uniffiRustCallAsync( uniffi_pubkymobile_fn_func_sign_out(
rustFutureFunc: { FfiConverterString.lower(secretKey),$0)
uniffi_pubkymobile_fn_func_sign_out( }
FfiConverterString.lower(secretKey)
)
},
pollFunc: ffi_pubkymobile_rust_future_poll_rust_buffer,
completeFunc: ffi_pubkymobile_rust_future_complete_rust_buffer,
freeFunc: ffi_pubkymobile_rust_future_free_rust_buffer,
liftFunc: FfiConverterSequenceString.lift,
errorHandler: nil
) )
} }
public func signUp(secretKey: String, homeserver: String) -> [String] {
return try! FfiConverterSequenceString.lift(
public func signUp(secretKey: String, homeserver: String) async -> [String] { try! rustCall() {
return try! await uniffiRustCallAsync( uniffi_pubkymobile_fn_func_sign_up(
rustFutureFunc: { FfiConverterString.lower(secretKey),
uniffi_pubkymobile_fn_func_sign_up( FfiConverterString.lower(homeserver),$0)
FfiConverterString.lower(secretKey), }
FfiConverterString.lower(homeserver)
)
},
pollFunc: ffi_pubkymobile_rust_future_poll_rust_buffer,
completeFunc: ffi_pubkymobile_rust_future_complete_rust_buffer,
freeFunc: ffi_pubkymobile_rust_future_free_rust_buffer,
liftFunc: FfiConverterSequenceString.lift,
errorHandler: nil
) )
} }
private enum InitializationResult { private enum InitializationResult {
case ok case ok
case contractVersionMismatch case contractVersionMismatch
@@ -617,7 +510,7 @@ private var initializationResult: InitializationResult {
if (uniffi_pubkymobile_checksum_func_generate_secret_key() != 63116) { if (uniffi_pubkymobile_checksum_func_generate_secret_key() != 63116) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_get() != 5395) { if (uniffi_pubkymobile_checksum_func_get() != 21596) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_get_public_key_from_secret_key() != 23603) { if (uniffi_pubkymobile_checksum_func_get_public_key_from_secret_key() != 23603) {
@@ -635,7 +528,7 @@ private var initializationResult: InitializationResult {
if (uniffi_pubkymobile_checksum_func_publish_https() != 14705) { if (uniffi_pubkymobile_checksum_func_publish_https() != 14705) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_put() != 47594) { if (uniffi_pubkymobile_checksum_func_put() != 51107) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_resolve() != 18303) { if (uniffi_pubkymobile_checksum_func_resolve() != 18303) {
@@ -644,17 +537,16 @@ private var initializationResult: InitializationResult {
if (uniffi_pubkymobile_checksum_func_resolve_https() != 34593) { if (uniffi_pubkymobile_checksum_func_resolve_https() != 34593) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_sign_in() != 53969) { if (uniffi_pubkymobile_checksum_func_sign_in() != 21006) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_sign_out() != 32961) { if (uniffi_pubkymobile_checksum_func_sign_out() != 59116) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
if (uniffi_pubkymobile_checksum_func_sign_up() != 28083) { if (uniffi_pubkymobile_checksum_func_sign_up() != 58756) {
return InitializationResult.apiChecksumMismatch return InitializationResult.apiChecksumMismatch
} }
uniffiInitContinuationCallback()
return InitializationResult.ok return InitializationResult.ok
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@synonymdev/react-native-pubky", "name": "@synonymdev/react-native-pubky",
"version": "0.7.0", "version": "0.7.1",
"description": "React Native Implementation of Pubky", "description": "React Native Implementation of Pubky",
"source": "./src/index.tsx", "source": "./src/index.tsx",
"main": "./lib/commonjs/index.js", "main": "./lib/commonjs/index.js",

View File

@@ -10,6 +10,7 @@ pub use utils::*;
uniffi::setup_scaffolding!(); uniffi::setup_scaffolding!();
use std::str;
use std::collections::HashMap; use std::collections::HashMap;
use base64::Engine; use base64::Engine;
use base64::engine::general_purpose; use base64::engine::general_purpose;
@@ -26,16 +27,9 @@ use serde_json::json;
use utils::*; use utils::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::Arc; use std::sync::Arc;
use pkarr::bytes::Bytes;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
static PKARR_CLIENT: Lazy<Arc<PkarrClient>> = Lazy::new(|| {
Arc::new(
PkarrClient::builder()
.build()
.expect("Failed to build PkarrClient"),
)
});
static PUBKY_CLIENT: Lazy<Arc<PubkyClient>> = Lazy::new(|| { static PUBKY_CLIENT: Lazy<Arc<PubkyClient>> = Lazy::new(|| {
Arc::new(PubkyClient::testnet()) Arc::new(PubkyClient::testnet())
}); });
@@ -87,209 +81,239 @@ pub fn get_public_key_from_secret_key(secret_key: String) -> Vec<String> {
#[uniffi::export] #[uniffi::export]
pub fn publish_https(record_name: String, target: String, secret_key: String) -> Vec<String> { pub fn publish_https(record_name: String, target: String, secret_key: String) -> Vec<String> {
let client = PKARR_CLIENT.clone(); let runtime = TOKIO_RUNTIME.clone();
runtime.block_on(async {
let client = PUBKY_CLIENT.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) { let keypair = match get_keypair_from_secret_key(&secret_key) {
Ok(keypair) => keypair, Ok(keypair) => keypair,
Err(error) => return create_response_vector(true, error), Err(error) => return create_response_vector(true, error),
}; };
// Create SVCB record with the target domain // Create SVCB record with the target domain
let target = match target.as_str().try_into() { let target = match target.as_str().try_into() {
Ok(target) => target, Ok(target) => target,
Err(e) => return create_response_vector(true, format!("Invalid target: {}", e)), Err(e) => return create_response_vector(true, format!("Invalid target: {}", e)),
}; };
let svcb = SVCB::new(0, target); let svcb = SVCB::new(0, target);
// Create HTTPS record // Create HTTPS record
let https_record = HTTPS(svcb); let https_record = HTTPS(svcb);
// Create DNS packet // Create DNS packet
let mut packet = Packet::new_reply(0); let mut packet = Packet::new_reply(0);
let dns_name = match dns::Name::new(&record_name) { let dns_name = match dns::Name::new(&record_name) {
Ok(name) => name, Ok(name) => name,
Err(e) => return create_response_vector(true, format!("Invalid DNS name: {}", e)), Err(e) => return create_response_vector(true, format!("Invalid DNS name: {}", e)),
}; };
packet.answers.push(ResourceRecord::new( packet.answers.push(ResourceRecord::new(
dns_name, dns_name,
dns::CLASS::IN, dns::CLASS::IN,
3600, // TTL in seconds 3600, // TTL in seconds
dns::rdata::RData::HTTPS(https_record), dns::rdata::RData::HTTPS(https_record),
)); ));
let signed_packet = match SignedPacket::from_packet(&keypair, &packet) { let signed_packet = match SignedPacket::from_packet(&keypair, &packet) {
Ok(signed_packet) => signed_packet, Ok(signed_packet) => signed_packet,
Err(e) => return create_response_vector(true, format!("Failed to create signed packet: {}", e)), Err(e) => return create_response_vector(true, format!("Failed to create signed packet: {}", e)),
}; };
match client.publish(&signed_packet) { match client.pkarr().publish(&signed_packet).await {
Ok(()) => create_response_vector(false, keypair.public_key().to_string()), Ok(()) => create_response_vector(false, keypair.public_key().to_string()),
Err(e) => create_response_vector(true, format!("Failed to publish: {}", e)), Err(e) => create_response_vector(true, format!("Failed to publish: {}", e)),
} }
})
} }
#[uniffi::export] #[uniffi::export]
pub fn resolve_https(public_key: String) -> Vec<String> { pub fn resolve_https(public_key: String) -> Vec<String> {
let public_key = match public_key.as_str().try_into() { let runtime = TOKIO_RUNTIME.clone();
Ok(key) => key, runtime.block_on(async {
Err(e) => return create_response_vector(true, format!("Invalid public key: {}", e)), let public_key = match public_key.as_str().try_into() {
}; Ok(key) => key,
Err(e) => return create_response_vector(true, format!("Invalid public key: {}", e)),
};
let client = PKARR_CLIENT.clone(); let client = PUBKY_CLIENT.clone();
match client.resolve(&public_key) { match client.pkarr().resolve(&public_key).await {
Ok(Some(signed_packet)) => { Ok(Some(signed_packet)) => {
// Extract HTTPS records from the signed packet // Extract HTTPS records from the signed packet
let https_records: Vec<serde_json::Value> = signed_packet.packet().answers.iter() let https_records: Vec<serde_json::Value> = signed_packet.packet().answers.iter()
.filter_map(|record| { .filter_map(|record| {
if let dns::rdata::RData::HTTPS(https) = &record.rdata { if let dns::rdata::RData::HTTPS(https) = &record.rdata {
// Create a JSON object // Create a JSON object
let mut https_json = serde_json::json!({ let mut https_json = serde_json::json!({
"name": record.name.to_string(), "name": record.name.to_string(),
"class": format!("{:?}", record.class), "class": format!("{:?}", record.class),
"ttl": record.ttl, "ttl": record.ttl,
"priority": https.0.priority, "priority": https.0.priority,
"target": https.0.target.to_string(), "target": https.0.target.to_string(),
}); });
// Access specific parameters using the constants from SVCB // Access specific parameters using the constants from SVCB
if let Some(port_param) = https.0.get_param(SVCB::PORT) { if let Some(port_param) = https.0.get_param(SVCB::PORT) {
if port_param.len() == 2 { if port_param.len() == 2 {
let port = u16::from_be_bytes([port_param[0], port_param[1]]); let port = u16::from_be_bytes([port_param[0], port_param[1]]);
https_json["port"] = serde_json::json!(port); https_json["port"] = serde_json::json!(port);
}
}
// Access ALPN parameter if needed
if let Some(alpn_param) = https.0.get_param(SVCB::ALPN) {
// Parse ALPN protocols (list of character strings)
let mut position = 0;
let mut alpn_protocols = Vec::new();
while position < alpn_param.len() {
let length = alpn_param[position] as usize;
position += 1;
if position + length <= alpn_param.len() {
let protocol = String::from_utf8_lossy(
&alpn_param[position..position + length],
);
alpn_protocols.push(protocol.to_string());
position += length;
} else {
break; // Malformed ALPN parameter
} }
} }
https_json["alpn"] = serde_json::json!(alpn_protocols);
// Access ALPN parameter if needed
if let Some(alpn_param) = https.0.get_param(SVCB::ALPN) {
// Parse ALPN protocols (list of character strings)
let mut position = 0;
let mut alpn_protocols = Vec::new();
while position < alpn_param.len() {
let length = alpn_param[position] as usize;
position += 1;
if position + length <= alpn_param.len() {
let protocol = String::from_utf8_lossy(
&alpn_param[position..position + length],
);
alpn_protocols.push(protocol.to_string());
position += length;
} else {
break; // Malformed ALPN parameter
}
}
https_json["alpn"] = serde_json::json!(alpn_protocols);
}
// TODO: Add other parameters as needed.
Some(https_json)
} else {
None
} }
// TODO: Add other parameters as needed. })
Some(https_json) .collect();
} else {
None
}
})
.collect();
if https_records.is_empty() { if https_records.is_empty() {
return create_response_vector(true, "No HTTPS records found".to_string()); return create_response_vector(true, "No HTTPS records found".to_string());
}
// Create JSON response
let json_obj = json!({
"public_key": public_key.to_string(),
"https_records": https_records,
"last_seen": signed_packet.last_seen(),
"timestamp": signed_packet.timestamp(),
});
let json_str = match serde_json::to_string(&json_obj) {
Ok(json) => json,
Err(e) => return create_response_vector(true, format!("Failed to serialize JSON: {}", e)),
};
create_response_vector(false, json_str)
},
Ok(None) => create_response_vector(true, "No signed packet found".to_string()),
Err(e) => create_response_vector(true, format!("Failed to resolve: {}", e)),
}
})
}
#[uniffi::export]
pub fn sign_up(secret_key: String, homeserver: String) -> Vec<String> {
let runtime = TOKIO_RUNTIME.clone();
runtime.block_on(async {
let client = PUBKY_CLIENT.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) {
Ok(keypair) => keypair,
Err(error) => return create_response_vector(true, error),
};
let homeserver_public_key = match PublicKey::try_from(homeserver) {
Ok(key) => key,
Err(error) => return create_response_vector(true, format!("Invalid homeserver public key: {}", error)),
};
match client.signup(&keypair, &homeserver_public_key).await {
Ok(session) => create_response_vector(false, session.pubky().to_string()),
Err(error) => create_response_vector(true, format!("signup failure: {}", error)),
}
})
}
#[uniffi::export]
pub fn sign_in(secret_key: String) -> Vec<String> {
let runtime = TOKIO_RUNTIME.clone();
runtime.block_on(async {
let client = PUBKY_CLIENT.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) {
Ok(keypair) => keypair,
Err(error) => return create_response_vector(true, error),
};
match client.signin(&keypair).await {
Ok(_) => create_response_vector(false, "Sign in success".to_string()),
Err(error) => {
create_response_vector(true, format!("Failed to sign in: {}", error))
} }
// Create JSON response
let json_obj = json!({
"public_key": public_key.to_string(),
"https_records": https_records,
"last_seen": signed_packet.last_seen(),
"timestamp": signed_packet.timestamp(),
});
let json_str = match serde_json::to_string(&json_obj) {
Ok(json) => json,
Err(e) => return create_response_vector(true, format!("Failed to serialize JSON: {}", e)),
};
create_response_vector(false, json_str)
},
Ok(None) => create_response_vector(true, "No signed packet found".to_string()),
Err(e) => create_response_vector(true, format!("Failed to resolve: {}", e)),
}
}
#[uniffi::export]
pub async fn sign_up(secret_key: String, homeserver: String) -> Vec<String> {
let client = PUBKY_CLIENT.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) {
Ok(keypair) => keypair,
Err(error) => return create_response_vector(true, error),
};
let homeserver_public_key = match PublicKey::try_from(homeserver) {
Ok(key) => key,
Err(error) => return create_response_vector(true, format!("Invalid homeserver public key: {}", error)),
};
match client.signup(&keypair, &homeserver_public_key).await {
Ok(_) => create_response_vector(false, "signup success".to_string()),
Err(error) => create_response_vector(true, format!("signup failure: {}", error)),
}
}
#[uniffi::export]
pub async fn sign_in(secret_key: String) -> Vec<String> {
let client = PUBKY_CLIENT.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) {
Ok(keypair) => keypair,
Err(error) => return create_response_vector(true, error),
};
match client.signin(&keypair).await {
Ok(_) => create_response_vector(false, "Sign in success".to_string()),
Err(error) => {
create_response_vector(true, format!("Failed to sign in: {}", error))
} }
} })
} }
#[uniffi::export] #[uniffi::export]
pub async fn sign_out(secret_key: String) -> Vec<String> { pub fn sign_out(secret_key: String) -> Vec<String> {
let client = PUBKY_CLIENT.clone(); let runtime = TOKIO_RUNTIME.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) { runtime.block_on(async {
Ok(keypair) => keypair, let client = PUBKY_CLIENT.clone();
Err(error) => return create_response_vector(true, error), let keypair = match get_keypair_from_secret_key(&secret_key) {
}; Ok(keypair) => keypair,
match client.signout(&keypair.public_key()).await { Err(error) => return create_response_vector(true, error),
Ok(_) => create_response_vector(false, "Sign out success".to_string()), };
Err(error) => { match client.signout(&keypair.public_key()).await {
create_response_vector(true, format!("Failed to sign out: {}", error)) Ok(_) => create_response_vector(false, "Sign out success".to_string()),
Err(error) => {
create_response_vector(true, format!("Failed to sign out: {}", error))
}
} }
} })
} }
#[uniffi::export] #[uniffi::export]
pub async fn put(url: String, content: String) -> Vec<String> { pub fn put(url: String, content: String) -> Vec<String> {
let client = PUBKY_CLIENT.clone(); let runtime = TOKIO_RUNTIME.clone();
let parsed_url = match Url::parse(&url) { runtime.block_on(async {
Ok(url) => url, let client = PUBKY_CLIENT.clone();
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()), let trimmed_url = url.trim_end_matches('/');
}; let parsed_url = match Url::parse(&trimmed_url) {
match client.put(parsed_url, &content.as_bytes()).await { Ok(url) => url,
Ok(_) => create_response_vector(false, "Put success".to_string()), Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
Err(error) => { };
create_response_vector(true, format!("Failed to put: {}", error)) match client.put(parsed_url, &content.as_bytes()).await {
Ok(_) => create_response_vector(false, trimmed_url.to_string()),
Err(error) => {
create_response_vector(true, format!("Failed to put: {}", error))
}
} }
} })
} }
#[uniffi::export] #[uniffi::export]
pub async fn get(url: String) -> Vec<String> { pub fn get(url: String) -> Vec<String> {
let client = PUBKY_CLIENT.clone(); let runtime = TOKIO_RUNTIME.clone();
let parsed_url = match Url::parse(&url) { runtime.block_on(async {
Ok(url) => url, let client = PUBKY_CLIENT.clone();
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()), let trimmed_url = url.trim_end_matches('/');
}; let parsed_url = match Url::parse(&trimmed_url) {
match client.get(parsed_url).await { Ok(url) => url,
Ok(_) => create_response_vector(false, "Get success".to_string()), Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
Err(error) => { };
create_response_vector(true, format!("Failed to get: {}", error)) let result: Option<Bytes> = match client.get(parsed_url).await {
} Ok(res) => res,
} Err(_) => return create_response_vector(true, "Request failed".to_string()),
};
let bytes = match result {
Some(bytes) => bytes,
None => return create_response_vector(true, "No data returned".to_string()),
};
let string = match str::from_utf8(&bytes) {
Ok(s) => s.to_string(),
Err(_) => return create_response_vector(true, "Invalid UTF-8 sequence".to_string()),
};
create_response_vector(false, string)
})
} }
/** /**
@@ -300,115 +324,122 @@ pub async fn get(url: String) -> Vec<String> {
**/ **/
#[uniffi::export] #[uniffi::export]
pub fn resolve(public_key: String) -> Vec<String> { pub fn resolve(public_key: String) -> Vec<String> {
let public_key = match public_key.as_str().try_into() { let runtime = TOKIO_RUNTIME.clone();
Ok(key) => key, runtime.block_on(async {
Err(e) => return create_response_vector(true, format!("Invalid zbase32 encoded key: {}", e)), let public_key = match public_key.as_str().try_into() {
}; Ok(key) => key,
let client = PKARR_CLIENT.clone(); Err(e) => return create_response_vector(true, format!("Invalid zbase32 encoded key: {}", e)),
};
let client = PUBKY_CLIENT.clone();
match client.resolve(&public_key) { match client.pkarr().resolve(&public_key).await {
Ok(Some(signed_packet)) => { Ok(Some(signed_packet)) => {
// Collect references to ResourceRecords from the signed packet's answers // Collect references to ResourceRecords from the signed packet's answers
let all_records: Vec<&ResourceRecord> = signed_packet.packet().answers.iter().collect(); let all_records: Vec<&ResourceRecord> = signed_packet.packet().answers.iter().collect();
// Convert each ResourceRecord to a JSON value, handling errors appropriately // Convert each ResourceRecord to a JSON value, handling errors appropriately
let json_records: Vec<serde_json::Value> = all_records let json_records: Vec<serde_json::Value> = all_records
.iter() .iter()
.filter_map(|record| { .filter_map(|record| {
match resource_record_to_json(record) { match resource_record_to_json(record) {
Ok(json_value) => Some(json_value), Ok(json_value) => Some(json_value),
Err(e) => { Err(e) => {
eprintln!("Error converting record to JSON: {}", e); eprintln!("Error converting record to JSON: {}", e);
None None
}
} }
} })
}) .collect();
.collect();
let bytes = signed_packet.as_bytes(); let bytes = signed_packet.as_bytes();
let public_key = &bytes[..32]; let public_key = &bytes[..32];
let signature = &bytes[32..96]; let signature = &bytes[32..96];
let timestamp = signed_packet.timestamp(); let timestamp = signed_packet.timestamp();
let dns_packet = &bytes[104..]; let dns_packet = &bytes[104..];
let hex: String = signed_packet.encode_hex(); let hex: String = signed_packet.encode_hex();
let json_obj = json!({ let json_obj = json!({
"signed_packet": hex, "signed_packet": hex,
"public_key": general_purpose::STANDARD.encode(public_key), "public_key": general_purpose::STANDARD.encode(public_key),
"signature": general_purpose::STANDARD.encode(signature), "signature": general_purpose::STANDARD.encode(signature),
"timestamp": timestamp, "timestamp": timestamp,
"last_seen": signed_packet.last_seen(), "last_seen": signed_packet.last_seen(),
"dns_packet": general_purpose::STANDARD.encode(dns_packet), "dns_packet": general_purpose::STANDARD.encode(dns_packet),
"records": json_records "records": json_records
}); });
let json_str = serde_json::to_string(&json_obj) let json_str = serde_json::to_string(&json_obj)
.expect("Failed to convert JSON object to string"); .expect("Failed to convert JSON object to string");
create_response_vector(false, json_str) create_response_vector(false, json_str)
}, },
Ok(None) => { Ok(None) => {
create_response_vector(true, "No signed packet found".to_string()) create_response_vector(true, "No signed packet found".to_string())
}
Err(e) => {
create_response_vector(true, format!("Failed to resolve: {}", e))
}
} }
Err(e) => { })
create_response_vector(true, format!("Failed to resolve: {}", e))
}
}
} }
#[uniffi::export] #[uniffi::export]
pub fn publish(record_name: String, record_content: String, secret_key: String) -> Vec<String> { pub fn publish(record_name: String, record_content: String, secret_key: String) -> Vec<String> {
let client = PKARR_CLIENT.clone(); let runtime = TOKIO_RUNTIME.clone();
runtime.block_on(async {
let client = PUBKY_CLIENT.clone();
let keypair = match get_keypair_from_secret_key(&secret_key) { let keypair = match get_keypair_from_secret_key(&secret_key) {
Ok(keypair) => keypair, Ok(keypair) => keypair,
Err(error) => return create_response_vector(true, error), Err(error) => return create_response_vector(true, error),
}; };
let mut packet = dns::Packet::new_reply(0); let mut packet = dns::Packet::new_reply(0);
let dns_name = match dns::Name::new(&record_name) { let dns_name = match dns::Name::new(&record_name) {
Ok(name) => name, Ok(name) => name,
Err(e) => return create_response_vector(true, format!("Failed to create DNS name: {}", e)), Err(e) => return create_response_vector(true, format!("Failed to create DNS name: {}", e)),
}; };
let record_content_str: &str = record_content.as_str(); let record_content_str: &str = record_content.as_str();
let txt_record = match record_content_str.try_into() { let txt_record = match record_content_str.try_into() {
Ok(value) => RData::TXT(value), Ok(value) => RData::TXT(value),
Err(e) => { Err(e) => {
return create_response_vector(true, format!("Failed to convert string to TXT record: {}", e)) return create_response_vector(true, format!("Failed to convert string to TXT record: {}", e))
} }
}; };
packet.answers.push(dns::ResourceRecord::new( packet.answers.push(dns::ResourceRecord::new(
dns_name, dns_name,
dns::CLASS::IN, dns::CLASS::IN,
30, 30,
txt_record, txt_record,
)); ));
match SignedPacket::from_packet(&keypair, &packet) { match SignedPacket::from_packet(&keypair, &packet) {
Ok(signed_packet) => { Ok(signed_packet) => {
match client.publish(&signed_packet) { match client.pkarr().publish(&signed_packet).await {
Ok(()) => { Ok(()) => {
create_response_vector(false, keypair.public_key().to_string()) create_response_vector(false, keypair.public_key().to_string())
} }
Err(e) => { Err(e) => {
create_response_vector(true, format!("Failed to publish: {}", e)) create_response_vector(true, format!("Failed to publish: {}", e))
}
} }
} }
Err(e) => {
create_response_vector(true, format!("Failed to create signed packet: {}", e))
}
} }
Err(e) => { })
create_response_vector(true, format!("Failed to create signed packet: {}", e))
}
}
} }
#[uniffi::export] #[uniffi::export]
pub fn list(url: String) -> Vec<String> { pub fn list(url: String) -> Vec<String> {
let runtime = TOKIO_RUNTIME.clone(); let runtime = TOKIO_RUNTIME.clone();
runtime.block_on(async { runtime.block_on(async {
let client = PUBKY_CLIENT.clone(); let client = PUBKY_CLIENT.clone();
let parsed_url = match Url::parse(&url) { let trimmed_url = url.trim_end_matches('/');
let parsed_url = match Url::parse(&trimmed_url) {
Ok(url) => url, Ok(url) => url,
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()), Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
}; };

View File

@@ -201,3 +201,44 @@ pub fn resource_record_to_json(record: &ResourceRecord) -> Result<serde_json::Va
"cache_flush": record.cache_flush "cache_flush": record.cache_flush
})) }))
} }
pub fn construct_pubky_url(public_key: &str, domain: &str, path_segments: &[&str]) -> String {
// Construct the base URL
let mut url = format!("pubky://{}/pub/{}", public_key, domain);
// Append each path segment, separated by '/'
for segment in path_segments {
if !segment.is_empty() {
url.push('/');
url.push_str(segment);
}
}
// Remove trailing slash if present
if url.ends_with('/') {
url.pop();
}
url
}
/**
* Extract everything up to the first instance of "pub/" in a Pubky URL
*
* # Arguments
* * `full_url` - The full URL
*
* # Returns
* * `Some(String)` - The "pub/" part of the URL
* * `None` - If "pub/" is not found in the URL
*/
pub fn get_list_url(full_url: &str) -> Option<String> {
if let Some(index) = full_url.find("pub/") {
let end_index = index + "pub/".len();
let substring = &full_url[..end_index];
Some(substring.to_string())
} else {
// "pub/" not found in the string
None
}
}

View File

@@ -2,10 +2,11 @@ use std::string::ToString;
use std::sync::Arc; use std::sync::Arc;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pkarr::{dns, Keypair, PublicKey, SignedPacket}; use pkarr::{dns, Keypair, PublicKey, SignedPacket};
use pkarr::bytes::Bytes;
use pkarr::dns::rdata::RData; use pkarr::dns::rdata::RData;
use pkarr::mainline::Testnet;
use pubky::PubkyClient; use pubky::PubkyClient;
use url::Url; use url::Url;
use std::str;
static PUBKY_CLIENT: Lazy<Arc<PubkyClient>> = Lazy::new(|| { static PUBKY_CLIENT: Lazy<Arc<PubkyClient>> = Lazy::new(|| {
// let custom_testnet = Testnet { // let custom_testnet = Testnet {
@@ -43,15 +44,55 @@ static PUBKY_CLIENT: Lazy<Arc<PubkyClient>> = Lazy::new(|| {
const HOMESERVER: &str = "pubky://8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"; const HOMESERVER: &str = "pubky://8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo";
const SECRET_KEY: &str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; const SECRET_KEY: &str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
fn construct_pubky_url(public_key: &str, domain: &str, path_segments: &[&str]) -> String {
// Construct the base URL
let mut url = format!("pubky://{}/pub/{}", public_key, domain);
// Append each path segment, separated by '/'
for segment in path_segments {
if !segment.is_empty() {
url.push('/');
url.push_str(segment);
}
}
// Remove trailing slash if present
if url.ends_with('/') {
url.pop();
}
url
}
fn get_list_url(full_url: &str) -> Option<String> {
if let Some(index) = full_url.find("pub/") {
// Add length of "pub/" to include it in the substring
let end_index = index + "pub/".len();
let substring = &full_url[..end_index];
Some(substring.to_string())
} else {
// "pub/" not found in the string
None
}
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let sign_in_res = signin_or_signup(SECRET_KEY, HOMESERVER).await; let sign_in_res = signin_or_signup(SECRET_KEY, HOMESERVER).await;
println!("{:?}", sign_in_res); println!("Sign In/Up Response: {:?}", sign_in_res);
// let res = publish("recordname".to_string(), "recordcontent".to_string(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855".to_string()).await; // let res = publish("recordname".to_string(), "recordcontent".to_string(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855".to_string()).await;
// println!("{:?}", res); // // println!("{:?}", res);
let url = "pubky://z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty/pub/mydomain.com"; let public_key = &sign_in_res[1];
let putRes = put(url.to_string(), "content".to_string()).await; let url = construct_pubky_url(public_key, "mydomain.com", &[]);
println!("{:?}", putRes); let put_res = put(&url, &"newcontent".to_string()).await;
println!("Put Response: {:?}", put_res);
let get_res = get(&url).await;
println!("Get Response: {:?}", get_res);
let list_res = list(url).await;
println!("List Response: {:?}", list_res);
} }
pub async fn signin_or_signup(secret_key: &str, homeserver: &str) -> Vec<String> { pub async fn signin_or_signup(secret_key: &str, homeserver: &str) -> Vec<String> {
@@ -76,7 +117,7 @@ pub async fn sign_up(secret_key: &str, homeserver: &str) -> Vec<String> {
}; };
match client.signup(&keypair, &homeserver_public_key).await { match client.signup(&keypair, &homeserver_public_key).await {
Ok(session) => create_response_vector(false, session.pubky().to_uri_string()), Ok(session) => create_response_vector(false, session.pubky().to_string()),
Err(error) => create_response_vector(true, format!("signup failure: {}", error)), Err(error) => create_response_vector(true, format!("signup failure: {}", error)),
} }
} }
@@ -89,7 +130,7 @@ pub async fn sign_in(secret_key: &str) -> Vec<String> {
}; };
match client.signin(&keypair).await { match client.signin(&keypair).await {
Ok(session) => { Ok(session) => {
create_response_vector(false, session.pubky().to_uri_string()) create_response_vector(false, session.pubky().to_string())
}, },
Err(error) => { Err(error) => {
create_response_vector(true, format!("Failed to sign in: {}", error)) create_response_vector(true, format!("Failed to sign in: {}", error))
@@ -169,16 +210,73 @@ pub fn create_response_vector(error: bool, data: String) -> Vec<String> {
} }
} }
pub async fn put(url: String, content: String) -> Vec<String> { pub async fn put(url: &String, content: &String) -> Vec<String> {
let client = PUBKY_CLIENT.clone(); let client = PUBKY_CLIENT.clone();
let parsed_url = match Url::parse(&url) { let trimmed_url = url.trim_end_matches('/');
let parsed_url = match Url::parse(&trimmed_url) {
Ok(url) => url, Ok(url) => url,
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()), Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
}; };
match client.put(parsed_url, &content.as_bytes()).await { match client.put(parsed_url, &content.as_bytes()).await {
Ok(_) => create_response_vector(false, "put success".to_string()), Ok(_) => create_response_vector(false, trimmed_url.to_string()),
Err(error) => { Err(error) => {
create_response_vector(true, format!("Failed to put: {}", error)) create_response_vector(true, format!("Failed to put: {}", error))
} }
} }
} }
pub async fn get(url: &String) -> Vec<String> {
let client = PUBKY_CLIENT.clone();
let trimmed_url = url.trim_end_matches('/');
// Parse the URL and return error early if it fails
let parsed_url = match Url::parse(&trimmed_url) {
Ok(url) => url,
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
};
// Perform the request and return error early if no data is returned
let result: Option<Bytes> = match client.get(parsed_url).await {
Ok(res) => res,
Err(_) => return create_response_vector(true, "Request failed".to_string()),
};
// If there are bytes, attempt to convert to UTF-8
let bytes = match result {
Some(bytes) => bytes,
None => return create_response_vector(true, "No data returned".to_string()),
};
// Try to convert bytes to string and return error if it fails
let string = match str::from_utf8(&bytes) {
Ok(s) => s.to_string(),
Err(_) => return create_response_vector(true, "Invalid UTF-8 sequence".to_string()),
};
// If everything is successful, return the formatted response
create_response_vector(false, string)
}
pub async fn list(url: String) -> Vec<String> {
let client = PUBKY_CLIENT.clone();
let trimmed_url = url.trim_end_matches('/');
let parsed_url = match Url::parse(&trimmed_url) {
Ok(url) => url,
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
};
let list_builder = match client.list(parsed_url) {
Ok(list) => list,
Err(error) => return create_response_vector(true, format!("Failed to list: {}", error)),
};
// Execute the non-Send part synchronously
let send_future = list_builder.send();
let send_res = match send_future.await {
Ok(res) => res,
Err(error) => return create_response_vector(true, format!("Failed to send list request: {}", error))
};
let json_string = match serde_json::to_string(&send_res) {
Ok(json) => json,
Err(error) => return create_response_vector(true, format!("Failed to serialize JSON: {}", error)),
};
create_response_vector(false, json_string)
}