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
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion examples/cpp/callback_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,15 @@ std::string get_event_type_name(EventType type)
// Type-specific callbacks - each receives only relevant parameters!
// =============================================================================

// Document callback - receives doc_id, title, content
// Document callback - receives doc_id, title, content, user_id, author_name, visibility
void document_event_callback(
EventType event_type,
const char* document_id,
const char* title,
const char* content,
const char* user_id,
const char* author_name,
const char* visibility,
void* context)
{
std::string event_name = get_event_type_name(event_type);
Expand All @@ -117,6 +120,14 @@ void document_event_callback(
{
std::cout << " - Title: '" << title << "'";
}
if (author_name)
{
std::cout << " - Author: " << author_name;
}
if (visibility)
{
std::cout << " - Visibility: " << visibility;
}
std::cout << "\n";
}

Expand Down
6 changes: 4 additions & 2 deletions replicant-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "replicant-client"
version = "0.2.0"
version = "0.3.0"
edition = "2021"

[dependencies]
Expand Down Expand Up @@ -37,7 +37,9 @@ name = "phoenix_spike"
path = "examples/phoenix_spike.rs"

[build-dependencies]
cbindgen = "0.26"
# Exact pin: the tracked replicant-client/include/replicant.h must be regenerated
# with this exact cbindgen version to avoid banner/whitespace churn on commit.
cbindgen = "=0.26.0"

[lib]
crate-type = ["lib", "staticlib", "cdylib"]
3 changes: 3 additions & 0 deletions replicant-client/examples/interactive_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ async fn create_task(
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
deleted_at: None,
author_name: None,
visibility: None,
provenance: None,
};

db.save_document(&doc).await?;
Expand Down
3 changes: 3 additions & 0 deletions replicant-client/examples/task_list_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,9 @@ async fn create_sample_task(
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
deleted_at: None,
author_name: None,
visibility: None,
provenance: None,
};

let _ = db.save_document(&doc).await;
Expand Down
3 changes: 3 additions & 0 deletions replicant-client/examples/test_rust_callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
deleted_at: None,
author_name: None,
visibility: None,
provenance: None,
};

db.save_document(&doc).await?;
Expand Down
38 changes: 31 additions & 7 deletions replicant-client/include/replicant.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#pragma once

/* Generated with cbindgen:0.29.0 */
/* Generated with cbindgen:0.26.0 */

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

Expand All @@ -16,7 +16,7 @@

#ifdef __cplusplus
namespace replicant {
#endif // __cplusplus
#endif // __cplusplus

/**
* Event types that can be emitted by the sync client
Expand Down Expand Up @@ -89,12 +89,18 @@ typedef struct Replicant Replicant;
* * `document_id` - UUID of the document (always non-null)
* * `title` - Document title (null for Deleted events)
* * `content` - Full document JSON (null for Deleted events)
* * `user_id` - Owner UUID (null if unknown)
* * `author_name` - Author display name (null if unknown)
* * `visibility` - "private"/"public" (null if unknown)
* * `context` - User-defined context pointer
*/
typedef void (*DocumentEventCallback)(enum ReplicantEventType event_type,
const char *document_id,
const char *title,
const char *content,
const char *user_id,
const char *author_name,
const char *visibility,
void *context);

/**
Expand Down Expand Up @@ -281,6 +287,24 @@ enum ReplicantSyncResult replicant_delete_document(struct Replicant *engine,
*/
void replicant_string_free(char *s);

/**
* Get the engine's own frozen user UUID
*
* # Arguments
* * `engine` - Sync engine instance
* * `out_user_id` - Output pointer for user UUID string (caller must free with replicant_string_free)
*
* # Returns
* * SyncResult::Success if the user ID was retrieved
* * SyncResult::ErrorInvalidInput if engine or out_user_id is null
* * SyncResult::ErrorDatabase if the user ID could not be read
*
* # Safety
* Caller must ensure engine is valid and out_user_id is a valid pointer
*/
enum ReplicantSyncResult replicant_get_user_id(struct Replicant *engine,
char **out_user_id);

/**
* Get library version string
*/
Expand Down Expand Up @@ -591,11 +615,11 @@ enum ReplicantSyncResult replicant_emit_test_event(struct Replicant *engine, int
enum ReplicantSyncResult replicant_emit_test_event_burst(struct Replicant *engine, int32_t count);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
} // extern "C"
#endif // __cplusplus

#ifdef __cplusplus
} // namespace replicant
#endif // __cplusplus
} // namespace replicant
#endif // __cplusplus

#endif /* REPLICANT_H */
#endif /* REPLICANT_H */
22 changes: 21 additions & 1 deletion replicant-client/include/replicant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,24 @@ class Client
check_result(result);
}

/**
* Get the engine's own frozen user UUID
*
* @return User UUID string
* @throws SyncException if retrieval fails
*/
std::string get_user_id()
{
char* user_id = nullptr;
SyncResult result = replicant_get_user_id(handle.get(), &user_id);

check_result(result);

std::string id(user_id);
replicant_string_free(user_id);
return id;
}

/**
* Get the library version
*
Expand Down Expand Up @@ -310,7 +328,9 @@ class Client
/**
* Register a callback for document events (Created, Updated, Deleted)
*
* @param callback Function to call for document events
* @param callback Function to call for document events. Receives
* (event_type, document_id, title, content, user_id, author_name, visibility, context).
* user_id, author_name, and visibility are null when unknown/not yet synced.
* @param context User-defined context pointer passed to callback
* @param event_filter Optional filter: 0=Created, 1=Updated, 2=Deleted, -1=all
* @throws SyncException if registration fails
Expand Down
3 changes: 3 additions & 0 deletions replicant-client/migrations/010_add_attribution.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE documents ADD COLUMN author_name TEXT;
ALTER TABLE documents ADD COLUMN visibility TEXT;
ALTER TABLE documents ADD COLUMN provenance JSON;
69 changes: 62 additions & 7 deletions replicant-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,9 @@ impl Client {
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
deleted_at: None,
author_name: None,
visibility: None,
provenance: None,
};

tracing::info!(
Expand All @@ -387,7 +390,13 @@ impl Client {
.await?;

self.event_dispatcher
.emit_document_created(&doc.id, &doc.content);
.emit_document_created_with_attribution(
&doc.id,
&doc.content,
doc.user_id.as_ref(),
doc.author_name.as_deref(),
doc.visibility.as_deref(),
);

if let Err(e) = self.try_immediate_sync(&doc).await {
tracing::warn!(
Expand Down Expand Up @@ -499,7 +508,13 @@ impl Client {

// Emit event
self.event_dispatcher
.emit_document_updated(&doc.id, &doc.content);
.emit_document_updated_with_attribution(
&doc.id,
&doc.content,
doc.user_id.as_ref(),
doc.author_name.as_deref(),
doc.visibility.as_deref(),
);

// Attempt immediate sync if connected
tracing::info!(
Expand Down Expand Up @@ -1076,7 +1091,13 @@ impl Client {
db.mark_synced(&doc.id).await?;

// Emit event for updated document
event_dispatcher.emit_document_updated(&doc.id, &doc.content);
event_dispatcher.emit_document_updated_with_attribution(
&doc.id,
&doc.content,
doc.user_id.as_ref(),
doc.author_name.as_deref(),
doc.visibility.as_deref(),
);
}
ServerMessage::DocumentCreated { document } => {
// New document from server - check if we already have it to avoid duplicates
Expand All @@ -1099,7 +1120,13 @@ impl Client {
.await?;

// Emit event for updated document
event_dispatcher.emit_document_updated(&document.id, &document.content);
event_dispatcher.emit_document_updated_with_attribution(
&document.id,
&document.content,
document.user_id.as_ref(),
document.author_name.as_deref(),
document.visibility.as_deref(),
);
}
}
Err(_) => {
Expand All @@ -1112,7 +1139,13 @@ impl Client {
.await?;

// Emit event for new document from server
event_dispatcher.emit_document_created(&document.id, &document.content);
event_dispatcher.emit_document_created_with_attribution(
&document.id,
&document.content,
document.user_id.as_ref(),
document.author_name.as_deref(),
document.visibility.as_deref(),
);
}
}
}
Expand Down Expand Up @@ -1201,7 +1234,13 @@ impl Client {
.await?;

// Emit event for updated document
event_dispatcher.emit_document_updated(&document.id, &document.content);
event_dispatcher.emit_document_updated_with_attribution(
&document.id,
&document.content,
document.user_id.as_ref(),
document.author_name.as_deref(),
document.visibility.as_deref(),
);
} else {
tracing::info!(
"CLIENT {}: Skipping older sync (local version {} >= sync version {})",
Expand All @@ -1222,7 +1261,13 @@ impl Client {
.await?;

// Emit event for new document
event_dispatcher.emit_document_created(&document.id, &document.content);
event_dispatcher.emit_document_created_with_attribution(
&document.id,
&document.content,
document.user_id.as_ref(),
document.author_name.as_deref(),
document.visibility.as_deref(),
);
}
}
}
Expand All @@ -1238,13 +1283,23 @@ impl Client {
document_id,
success,
error,
author_name,
visibility,
provenance,
} => {
if success {
tracing::info!(
"CLIENT {}: Document creation confirmed by server: {}",
client_id,
document_id
);
db.update_attribution(
&document_id,
author_name.clone(),
visibility.clone(),
provenance.clone(),
)
.await?;
db.mark_synced(&document_id).await?;
// Clean up sync_queue
db.remove_from_sync_queue(&document_id).await?;
Expand Down
Loading
Loading