Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ meson compile -C _build

### Use libp2panda in python
The tests contain a example on how to use libp2padna in python.

## Credits

Thanks a lot to Sophie Herold, for writing [libglycin](https://gitlab.gnome.org/GNOME/glycin) that was used as a base for figuring out how to write the bindings and Sergey Bugaev for reviewing the GLib introspection API.
133 changes: 106 additions & 27 deletions libp2panda/include/p2panda.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,63 @@

G_BEGIN_DECLS

/**
* P2pandaNode:bootstrap:
*
* A node that is used to find other nodes.
*/

/**
* P2pandaNode:database-url:
*
* The [uri](https://www.sqlite.org/uri.html) to a sqlite database.
*/

/**
* P2pandaNode:network-id:
*
* The id of the network to this node will join.
*/

/**
* P2pandaNode:private-key:
*
* The private key representing the node's identity.
*/

/**
* P2pandaNode:relay-url:
*
* The url to a [iroh relay](https://docs.iroh.computer/concepts/relays).
*/

/**
* P2pandaNode::system-event:
*
* Emitted on system events
* Emitted on system events.
*
* This signal is emitted on the main context that was thread default when the node was spawned.
*/

/**
* P2pandaTopic:node:
*
* A spawned [class@Node].
*
*/

/**
* P2pandaTopic:flags:
*
* Configuration for the [class@Topic].
*
*/

/**
* P2pandaTopic:topic-id:
*
* The id of the topic this [class@Topic] will be subscribed to.
*
*/

/**
Expand All @@ -19,7 +72,9 @@ G_BEGIN_DECLS
* @datetime: The timestamp
* @bytes: The message
*
* Emitted on incomming persistent message
* Emitted on incomming persistent message.
*
* This signal is emitted on the main context that was thread default when the topic was spawned.
*/

/**
Expand All @@ -29,7 +84,9 @@ G_BEGIN_DECLS
* @datetime: The timestamp
* @bytes: The content of the message
*
* Emitted on incomming ephemeral message
* Emitted on incomming ephemeral message.
*
* This signal is emitted on the main context that was thread default when the topic was spawned.
*/

/**
Expand All @@ -42,7 +99,9 @@ G_BEGIN_DECLS
* @incoming_bytes:
* @outgoing_bytes:
*
* Emitted for errors
* Emitted for errors.
*
* This signal is emitted on the main context that was thread default when the topic was spawned.
*/

/**
Expand All @@ -51,15 +110,19 @@ G_BEGIN_DECLS
* @remote_node_id: The public key for the remote node
* @session_id:
*
* Emitted when a topic finishes syncing
* Emitted when a topic finishes syncing.
*
* This signal is emitted on the main context that was thread default when the topic was spawned.
*/

/**
* P2pandaTopic::error:
* @topic:
* @error:
*
* Emitted when a topic finishes syncing
* Emitted when a topic finishes syncing.
*
* This signal is emitted on the main context that was thread default when the topic was spawned.
*/

/**
Expand Down Expand Up @@ -160,18 +223,18 @@ const uint8_t* p2panda_node_id_get_data(P2pandaNodeId *node_id);

/**
* P2pandaTopicFlags:
* @P2PANDA_TOPIC_FLAGS_NONE:
* @P2PANDA_TOPIC_FLAGS_PERSISTENT:
* @P2PANDA_TOPIC_FLAGS_EPHEMERAL:
* @P2PANDA_TOPIC_FLAGS_FROM_START:
* @P2PANDA_TOPIC_NONE:
* @P2PANDA_TOPIC_PERSISTENT:
* @P2PANDA_TOPIC_EPHEMERAL:
* @P2PANDA_TOPIC_FROM_START:
*
*/
typedef enum
{
P2PANDA_TOPIC_FLAGS_NONE,
P2PANDA_TOPIC_FLAGS_PERSISTENT,
P2PANDA_TOPIC_FLAGS_EPHEMERAL,
P2PANDA_TOPIC_FLAGS_FROM_START,
P2PANDA_TOPIC_NONE = 0,
P2PANDA_TOPIC_PERSISTENT = 1 << 0,
P2PANDA_TOPIC_EPHEMERAL = 1 << 1,
P2PANDA_TOPIC_FROM_START = 1 << 2,
} P2pandaTopicFlags;

GType p2panda_topic_flags_get_type(void);
Expand Down Expand Up @@ -329,9 +392,14 @@ P2pandaNode *p2panda_node_new(P2pandaPrivateKey *private_key,
* p2panda_node_spawn_async:
* @node:
* @cancellable: (nullable):
* @callback:
* @callback: (nullable):
* @user_data: user data to pass to @callback
*
* Spawn the node.
*
* Before spawning the node no network communcation takes place, and the [class@Node]
* can't be used.
*
*/
void p2panda_node_spawn_async(P2pandaNode *node,
GCancellable *cancellable,
Expand Down Expand Up @@ -362,7 +430,7 @@ gboolean p2panda_node_spawn_finish(P2pandaNode *node,
*
* Create a topic handle for the give [class@Node]
*
* Returns: (transfer full): a new [class@Node]
* Returns: (transfer full): a new [class@Topic]
*/
P2pandaTopic *p2panda_topic_new(P2pandaNode *node,
P2pandaTopicId *topic_id,
Expand All @@ -372,9 +440,14 @@ P2pandaTopic *p2panda_topic_new(P2pandaNode *node,
* p2panda_topic_spawn_async:
* @topic:
* @cancellable: (nullable):
* @callback:
* @callback: (nullable):
* @user_data: user data to pass to @callback
*
* Spawn the topic.
*
* Before spawning the topic no network communcation takes place, and the [class@Topic]
* won't be able to send nor receive messages.
*
*/
void p2panda_topic_spawn_async(P2pandaTopic *topic,
GCancellable *cancellable,
Expand All @@ -398,12 +471,14 @@ gboolean p2panda_topic_spawn_finish(P2pandaTopic *topic,
/**
* p2panda_topic_publish_async:
* @topic:
* @bytes: (transfer full):
* @bytes: (transfer none):
* @ephemeral: Whether this message should be ephemeral or persistent
* @cancellable: (nullable):
* @callback:
* @callback (nullable):
* @user_data: user data to pass to @callback
*
* Publish a message to a topic.
*
*/
void p2panda_topic_publish_async(P2pandaTopic *topic,
GBytes *bytes,
Expand All @@ -413,7 +488,7 @@ void p2panda_topic_publish_async(P2pandaTopic *topic,
gpointer user_data);

/**
* p2panda_publish_publish_finish:
* p2panda_topic_publish_finish:
* @topic:
* @result: A [iface@Gio.AsyncResult]
* @error:
Expand All @@ -430,13 +505,17 @@ gboolean p2panda_topic_publish_finish(P2pandaTopic *topic,

/**
* P2pandaError:
* @P2PANDA_LOADER_ERROR_FAILED:
* @P2PANDA_LOADER_ERROR_UNKNOWN_IMAGE_FORMAT:
* @P2PANDA_LOADER_ERROR_NO_MORE_FRAMES:
*
* Errors that can appear while loading images.
*
* Since: 2.0
* @P2PANDA_ERROR_FAILED:
* @P2PANDA_ERROR_SPAWN_NODE:
* @P2PANDA_ERROR_SPAWN_TOPIC:
* @P2PANDA_ERROR_NOT_SPAWNED:
* @P2PANDA_ERROR_DECODING:
* @P2PANDA_ERROR_REPLAY:
* @P2PANDA_ERROR_HAS_NO_PERSISTENT:
* @P2PANDA_ERROR_PUBLISH:
* @P2PANDA_ERROR_SIGNATURE,
*
* Errors that may happen while interacting with a node and topic
*/
typedef enum
{
Expand Down
2 changes: 1 addition & 1 deletion libp2panda/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub unsafe extern "C" fn p2panda_private_key_new() -> *const identity::PrivateKe
#[unsafe(no_mangle)]
pub unsafe extern "C" fn p2panda_private_key_get_public_key(
private_key: *const identity::PrivateKey,
) -> *const identity::PublicKey {
) -> *mut identity::PublicKey {
unsafe {
let private_key = identity::PrivateKey::from_glib_none(private_key);
private_key.public_key().into_glib_ptr()
Expand Down
25 changes: 14 additions & 11 deletions libp2panda/src/node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::ffi::c_char;
use std::ffi::c_int;
use std::ops::Deref;

use futures::future::{AbortHandle, Abortable};
use gio::ffi::{GAsyncReadyCallback, GAsyncResult, GTask};
Expand Down Expand Up @@ -126,23 +127,23 @@ pub unsafe extern "C" fn p2panda_node_new(
mdns_mode: c_int,
) -> *mut P2pandaNode {
unsafe {
let private_key = Option::<identity::PrivateKey>::from_glib_none(private_key);
let private_key = Option::<identity::PrivateKey>::from_glib_borrow(private_key);
let database_url = if database_url.is_null() {
None
} else {
glib::GStr::from_ptr_checked(database_url)
};
let network_id = Option::<node::NetworkId>::from_glib_none(network_id);
let relay_url = Option::<glib::Uri>::from_glib_none(relay_url);
let bootstrap_node = Option::<node::NodeId>::from_glib_none(bootstrap_node);
let network_id = Option::<node::NetworkId>::from_glib_borrow(network_id);
let relay_url = Option::<glib::Uri>::from_glib_borrow(relay_url);
let bootstrap_node = Option::<node::NodeId>::from_glib_borrow(bootstrap_node);
let mdns_mode = node::MdnsDiscoveryMode::from_glib(mdns_mode);

node::Node::new(
private_key.as_ref(),
private_key.deref().as_ref(),
database_url,
network_id.as_ref(),
relay_url.as_ref(),
bootstrap_node.as_ref(),
network_id.deref().as_ref(),
relay_url.deref().as_ref(),
bootstrap_node.deref().as_ref(),
mdns_mode,
)
.into_glib_ptr()
Expand All @@ -159,7 +160,7 @@ pub unsafe extern "C" fn p2panda_node_spawn_async(
unsafe {
let obj = node::Node::from_glib_none(node);
let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
let callback = GAsyncReadyCallbackSend::new(callback, user_data);
let callback = callback.map(|callback| GAsyncReadyCallbackSend::new(callback, user_data));

let (abort_handle, abort_registration) = AbortHandle::new_pair();
let cancel_signal = if let Some(cancellable) = &cancellable {
Expand All @@ -174,8 +175,10 @@ pub unsafe extern "C" fn p2panda_node_spawn_async(
cancellable.disconnect_cancelled(cancel_signal);
}

let result = task.upcast_ref::<gio::AsyncResult>().as_ptr();
callback.call(obj.unwrap(), result);
if let Some(callback) = callback {
let result = task.upcast_ref::<gio::AsyncResult>().as_ptr();
callback.call(obj.unwrap(), result);
}
};

let task = gio::Task::new(Some(&obj), cancellable_.as_ref(), closure);
Expand Down
18 changes: 11 additions & 7 deletions libp2panda/src/topic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub unsafe extern "C" fn p2panda_topic_spawn_async(
unsafe {
let obj = topic::Topic::from_glib_none(topic);
let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
let callback = GAsyncReadyCallbackSend::new(callback, user_data);
let callback = callback.map(|callback| GAsyncReadyCallbackSend::new(callback, user_data));

let (abort_handle, abort_registration) = AbortHandle::new_pair();
let cancel_signal = if let Some(cancellable) = &cancellable {
Expand All @@ -91,8 +91,10 @@ pub unsafe extern "C" fn p2panda_topic_spawn_async(
cancellable.disconnect_cancelled(cancel_signal);
}

let result = task.upcast_ref::<gio::AsyncResult>().as_ptr();
callback.call(obj.unwrap(), result);
if let Some(callback) = callback {
let result = task.upcast_ref::<gio::AsyncResult>().as_ptr();
callback.call(obj.unwrap(), result);
}
};

let task = gio::Task::new(Some(&obj), cancellable_.as_ref(), closure);
Expand Down Expand Up @@ -142,9 +144,9 @@ pub unsafe extern "C" fn p2panda_topic_publish_async(
) {
unsafe {
let obj = topic::Topic::from_glib_none(topic);
let bytes = glib::Bytes::from_glib_full(bytes);
let bytes = glib::Bytes::from_glib_none(bytes);
let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
let callback = GAsyncReadyCallbackSend::new(callback, user_data);
let callback = callback.map(|callback| GAsyncReadyCallbackSend::new(callback, user_data));

let (abort_handle, abort_registration) = AbortHandle::new_pair();
let cancel_signal = if let Some(cancellable) = &cancellable {
Expand All @@ -159,8 +161,10 @@ pub unsafe extern "C" fn p2panda_topic_publish_async(
cancellable.disconnect_cancelled(cancel_signal);
}

let result = task.upcast_ref::<gio::AsyncResult>().as_ptr();
callback.call(obj.unwrap(), result);
if let Some(callback) = callback {
let result = task.upcast_ref::<gio::AsyncResult>().as_ptr();
callback.call(obj.unwrap(), result);
}
};

let task = gio::Task::new(Some(&obj), cancellable_.as_ref(), closure);
Expand Down
14 changes: 6 additions & 8 deletions libp2panda/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
/* Taken from glycin https://gitlab.gnome.org/GNOME/glycin/-/blob/main/libglycin/src/common.rs */
use gio::ffi::GAsyncReadyCallback;
use gio::ffi::GAsyncResult;
use gio::prelude::*;
use glib::ffi::gpointer;
use glib::{ffi::gpointer, gobject_ffi::GObject};

type GAsyncReadyCallback = unsafe extern "C" fn(*mut GObject, *mut GAsyncResult, gpointer);

struct GPointerSend(pub gpointer);

unsafe impl Send for GPointerSend {}

pub struct GAsyncReadyCallbackSend {
callback: unsafe extern "C" fn(
*mut glib::gobject_ffi::GObject,
*mut gio::ffi::GAsyncResult,
gpointer,
),
callback: GAsyncReadyCallback,
user_data: GPointerSend,
}

Expand All @@ -21,7 +19,7 @@ unsafe impl Send for GAsyncReadyCallbackSend {}
impl GAsyncReadyCallbackSend {
pub fn new(callback: GAsyncReadyCallback, user_data: gpointer) -> Self {
Self {
callback: callback.unwrap(),
callback,
user_data: GPointerSend(user_data),
}
}
Expand Down
Loading
Loading