mirror of
https://github.com/logos-messaging/nim-chat-sdk.git
synced 2026-01-02 14:13:07 +00:00
feat(skelleton): skelleton with bindings
This commit is contained in:
parent
75a22367b9
commit
010a984c3e
54
Makefile
Normal file
54
Makefile
Normal file
@ -0,0 +1,54 @@
|
||||
# ChatSDK Makefile
|
||||
# This builds the complete chain: Nim -> C bindings -> Go bindings -> Go example
|
||||
|
||||
.PHONY: all clean build-nim build-c build-go run-go-example help
|
||||
|
||||
# Default target
|
||||
all: build-nim build-c build-go
|
||||
|
||||
# Help target
|
||||
help:
|
||||
@echo "ChatSDK Build System"
|
||||
@echo "===================="
|
||||
@echo "Available targets:"
|
||||
@echo " all - Build everything (Nim + C + Go)"
|
||||
@echo " build-nim - Build Nim library"
|
||||
@echo " build-c - Build C bindings (shared library)"
|
||||
@echo " build-go - Build Go bindings"
|
||||
@echo " run-go-example - Run the Go example application"
|
||||
@echo " clean - Clean all build artifacts"
|
||||
@echo " help - Show this help message"
|
||||
|
||||
# Build Nim library
|
||||
build-nim:
|
||||
@echo "Building Nim library..."
|
||||
cd src && nim c --app:lib --opt:speed --mm:arc --out:../bindings/c-bindings/libchatsdk.so chat_sdk.nim
|
||||
|
||||
# Build C bindings
|
||||
build-c: build-nim
|
||||
@echo "C bindings ready (built with Nim)"
|
||||
|
||||
# Build Go bindings (just verify they compile)
|
||||
build-go: build-c
|
||||
@echo "Building Go bindings..."
|
||||
cd bindings/go-bindings && go build .
|
||||
|
||||
# Run Go example
|
||||
run-go-example: build-go
|
||||
@echo "Running Go example..."
|
||||
cd examples/go-app && \
|
||||
LD_LIBRARY_PATH=../../bindings/c-bindings:$$LD_LIBRARY_PATH \
|
||||
go run main.go
|
||||
|
||||
# Clean all build artifacts
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
rm -f bindings/c-bindings/*.so bindings/c-bindings/*.a
|
||||
rm -rf src/nimcache bindings/c-bindings/nimcache
|
||||
cd bindings/go-bindings && go clean
|
||||
cd examples/go-app && go clean
|
||||
|
||||
# Test the Nim library directly
|
||||
test-nim:
|
||||
@echo "Testing Nim library directly..."
|
||||
cd src && nim r chat_sdk.nim
|
||||
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Chat SDK
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Build Everything
|
||||
```bash
|
||||
# Build the complete chain: Nim → C → Go
|
||||
make all
|
||||
|
||||
# Or step by step:
|
||||
make build-nim # Build Nim library to shared library
|
||||
make build-c # Prepare C bindings
|
||||
make build-go # Verify Go bindings compile
|
||||
```
|
||||
|
||||
### Run the Go Example
|
||||
```bash
|
||||
make run-go-example
|
||||
```
|
||||
35
bindings/c-bindings/Makefile
Normal file
35
bindings/c-bindings/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -fPIC
|
||||
NIMFLAGS = --app:lib --threads:on --gc:orc
|
||||
|
||||
# Directories
|
||||
SRC_DIR = ../../src
|
||||
BUILD_DIR = .
|
||||
|
||||
# Targets
|
||||
SHARED_LIB = libchatsdk.so
|
||||
STATIC_LIB = libchatsdk.a
|
||||
HEADER = chatsdk.h
|
||||
|
||||
.PHONY: all clean shared static
|
||||
|
||||
all: shared static
|
||||
|
||||
shared: $(SHARED_LIB)
|
||||
|
||||
static: $(STATIC_LIB)
|
||||
|
||||
$(SHARED_LIB): $(SRC_DIR)/chat_sdk.nim $(HEADER)
|
||||
cd $(SRC_DIR) && nim c $(NIMFLAGS) --out:../bindings/c-bindings/$(SHARED_LIB) chat_sdk.nim
|
||||
|
||||
$(STATIC_LIB): $(SRC_DIR)/chat_sdk.nim $(HEADER)
|
||||
cd $(SRC_DIR) && nim c $(NIMFLAGS) --app:staticLib --out:../bindings/c-bindings/$(STATIC_LIB) chat_sdk.nim
|
||||
|
||||
clean:
|
||||
rm -f $(SHARED_LIB) $(STATIC_LIB) *.o *.so *.a
|
||||
rm -rf nimcache
|
||||
|
||||
install: $(SHARED_LIB) $(HEADER)
|
||||
sudo cp $(SHARED_LIB) /usr/local/lib/
|
||||
sudo cp $(HEADER) /usr/local/include/
|
||||
sudo ldconfig
|
||||
64
bindings/c-bindings/chatsdk.h
Normal file
64
bindings/c-bindings/chatsdk.h
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef CHATSDK_H
|
||||
#define CHATSDK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Forward declaration for ChatSDK
|
||||
typedef struct ChatSDK ChatSDK;
|
||||
|
||||
// Storage interface function pointer types
|
||||
typedef int (*StoreMessageProc)(const char* id, const char* message, void* userData);
|
||||
typedef const char* (*GetMessageProc)(const char* id, void* userData);
|
||||
|
||||
/**
|
||||
* Send a message through the ChatSDK (standalone version)
|
||||
* @param message The message to send as a null-terminated string
|
||||
* @return 0 on success, non-zero on error
|
||||
*/
|
||||
int sendMessageCString(const char* message);
|
||||
|
||||
/**
|
||||
* Create a new ChatSDK instance with storage callbacks
|
||||
* @param storeProc Function pointer for storing messages
|
||||
* @param getProc Function pointer for retrieving messages
|
||||
* @param userData Optional user data pointer
|
||||
* @return Pointer to ChatSDK instance or NULL on error
|
||||
*/
|
||||
ChatSDK* newChatSDKC(StoreMessageProc storeProc, GetMessageProc getProc, void* userData);
|
||||
|
||||
/**
|
||||
* Free a ChatSDK instance
|
||||
* @param sdk Pointer to ChatSDK instance to free
|
||||
*/
|
||||
void freeChatSDKC(ChatSDK* sdk);
|
||||
|
||||
/**
|
||||
* Send a message through a ChatSDK instance
|
||||
* @param sdk Pointer to ChatSDK instance
|
||||
* @param id Message ID
|
||||
* @param message The message to send
|
||||
* @return 0 on success, non-zero on error
|
||||
*/
|
||||
int sendMessageSDKC(ChatSDK* sdk, const char* id, const char* message);
|
||||
|
||||
/**
|
||||
* Get a message from a ChatSDK instance
|
||||
* @param sdk Pointer to ChatSDK instance
|
||||
* @param id Message ID to retrieve
|
||||
* @return Message string (caller must call freeCString) or NULL if not found
|
||||
*/
|
||||
const char* getMessageSDKC(ChatSDK* sdk, const char* id);
|
||||
|
||||
/**
|
||||
* Free a C string allocated by the library
|
||||
* @param str String to free
|
||||
*/
|
||||
void freeCString(const char* str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CHATSDK_H
|
||||
BIN
bindings/c-bindings/libchatsdk.so
Executable file
BIN
bindings/c-bindings/libchatsdk.so
Executable file
Binary file not shown.
56
bindings/go-bindings/callbacks.go
Normal file
56
bindings/go-bindings/callbacks.go
Normal file
@ -0,0 +1,56 @@
|
||||
package chatsdk
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// goStoreMessage is called from C to store a message using the Go Store interface
|
||||
//
|
||||
//export goStoreMessage
|
||||
func goStoreMessage(cID *C.char, cMessage *C.char, userData unsafe.Pointer) C.int {
|
||||
if cID == nil || cMessage == nil {
|
||||
return 1 // Error
|
||||
}
|
||||
|
||||
store := getStoreFromUserData(userData)
|
||||
if store == nil {
|
||||
return 1 // Error
|
||||
}
|
||||
|
||||
id := C.GoString(cID)
|
||||
message := C.GoString(cMessage)
|
||||
|
||||
success := store.StoreMessage(id, message)
|
||||
if success {
|
||||
return 0 // Success
|
||||
}
|
||||
return 1 // Error
|
||||
}
|
||||
|
||||
// goGetMessage is called from C to retrieve a message using the Go Store interface
|
||||
//
|
||||
//export goGetMessage
|
||||
func goGetMessage(cID *C.char, userData unsafe.Pointer) *C.char {
|
||||
if cID == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
store := getStoreFromUserData(userData)
|
||||
if store == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
id := C.GoString(cID)
|
||||
message := store.GetMessage(id)
|
||||
|
||||
if message == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Allocate C string - Nim side will free this
|
||||
return C.CString(message)
|
||||
}
|
||||
169
bindings/go-bindings/chatsdk.go
Normal file
169
bindings/go-bindings/chatsdk.go
Normal file
@ -0,0 +1,169 @@
|
||||
package chatsdk
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I../c-bindings
|
||||
#cgo LDFLAGS: -L../c-bindings -lchatsdk
|
||||
#include "chatsdk.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// Forward declarations for the Go callback functions
|
||||
int goStoreMessage(const char* id, const char* message, void* userData);
|
||||
const char* goGetMessage(const char* id, void* userData);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Store interface that Go implementations must satisfy
|
||||
type Store interface {
|
||||
StoreMessage(id, message string) bool
|
||||
GetMessage(id string) string
|
||||
}
|
||||
|
||||
// ChatSDK represents a chat SDK instance with storage capabilities
|
||||
type ChatSDK struct {
|
||||
cSDK *C.ChatSDK
|
||||
store Store
|
||||
closed bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Global registry to map C callback calls back to Go Store implementations
|
||||
var (
|
||||
storeRegistry = make(map[uintptr]Store)
|
||||
registryMu sync.RWMutex
|
||||
nextID uintptr = 1
|
||||
)
|
||||
|
||||
// SendMessage sends a message through the ChatSDK (standalone version)
|
||||
func SendMessage(message string) error {
|
||||
cMessage := C.CString(message)
|
||||
defer C.free(unsafe.Pointer(cMessage))
|
||||
|
||||
result := C.sendMessageCString(cMessage)
|
||||
if result != 0 {
|
||||
return errors.New("failed to send message")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewChatSDK creates a new ChatSDK instance with the provided store implementation
|
||||
func NewChatSDK(store Store) (*ChatSDK, error) {
|
||||
if store == nil {
|
||||
return nil, errors.New("store cannot be nil")
|
||||
}
|
||||
|
||||
// Register the store implementation
|
||||
registryMu.Lock()
|
||||
storeID := nextID
|
||||
nextID++
|
||||
storeRegistry[storeID] = store
|
||||
registryMu.Unlock()
|
||||
|
||||
// Create the C SDK instance with callback function pointers
|
||||
userData := unsafe.Pointer(uintptr(storeID))
|
||||
cSDK := C.newChatSDKC(
|
||||
C.StoreMessageProc(C.goStoreMessage),
|
||||
C.GetMessageProc(C.goGetMessage),
|
||||
userData,
|
||||
)
|
||||
|
||||
if cSDK == nil {
|
||||
// Clean up registry on failure
|
||||
registryMu.Lock()
|
||||
delete(storeRegistry, storeID)
|
||||
registryMu.Unlock()
|
||||
return nil, errors.New("failed to create ChatSDK instance")
|
||||
}
|
||||
|
||||
sdk := &ChatSDK{
|
||||
cSDK: cSDK,
|
||||
store: store,
|
||||
}
|
||||
|
||||
// Set finalizer to ensure cleanup
|
||||
runtime.SetFinalizer(sdk, (*ChatSDK).Close)
|
||||
|
||||
return sdk, nil
|
||||
}
|
||||
|
||||
// SendMessage sends a message through this ChatSDK instance
|
||||
func (sdk *ChatSDK) SendMessage(id, message string) error {
|
||||
sdk.mu.RLock()
|
||||
defer sdk.mu.RUnlock()
|
||||
|
||||
if sdk.closed {
|
||||
return errors.New("ChatSDK instance is closed")
|
||||
}
|
||||
|
||||
cID := C.CString(id)
|
||||
cMessage := C.CString(message)
|
||||
defer C.free(unsafe.Pointer(cID))
|
||||
defer C.free(unsafe.Pointer(cMessage))
|
||||
|
||||
result := C.sendMessageSDKC(sdk.cSDK, cID, cMessage)
|
||||
if result != 0 {
|
||||
return errors.New("failed to send message")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMessage retrieves a message by ID through this ChatSDK instance
|
||||
func (sdk *ChatSDK) GetMessage(id string) (string, error) {
|
||||
sdk.mu.RLock()
|
||||
defer sdk.mu.RUnlock()
|
||||
|
||||
if sdk.closed {
|
||||
return "", errors.New("ChatSDK instance is closed")
|
||||
}
|
||||
|
||||
cID := C.CString(id)
|
||||
defer C.free(unsafe.Pointer(cID))
|
||||
|
||||
cResult := C.getMessageSDKC(sdk.cSDK, cID)
|
||||
if cResult == nil {
|
||||
return "", nil // Message not found
|
||||
}
|
||||
|
||||
result := C.GoString(cResult)
|
||||
C.freeCString(cResult) // Free the string allocated by Nim
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Close frees the ChatSDK instance and cleans up resources
|
||||
func (sdk *ChatSDK) Close() error {
|
||||
sdk.mu.Lock()
|
||||
defer sdk.mu.Unlock()
|
||||
|
||||
if sdk.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sdk.cSDK != nil {
|
||||
C.freeChatSDKC(sdk.cSDK)
|
||||
sdk.cSDK = nil
|
||||
}
|
||||
|
||||
sdk.closed = true
|
||||
runtime.SetFinalizer(sdk, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getStoreFromUserData retrieves a Store implementation from userData pointer
|
||||
func getStoreFromUserData(userData unsafe.Pointer) Store {
|
||||
if userData == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
storeID := uintptr(userData)
|
||||
registryMu.RLock()
|
||||
store := storeRegistry[storeID]
|
||||
registryMu.RUnlock()
|
||||
return store
|
||||
}
|
||||
3
bindings/go-bindings/go.mod
Normal file
3
bindings/go-bindings/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module github.com/waku-org/nim-chat-sdk/bindings/go-bindings
|
||||
|
||||
go 1.24
|
||||
@ -10,3 +10,9 @@ srcDir = "src"
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 2.0.0"
|
||||
|
||||
task buildSharedLib, "Build shared library for C bindings":
|
||||
exec "nim c --app:lib --out:../bindings/c-bindings/libchatsdk.so src/chat_sdk.nim"
|
||||
|
||||
task buildStaticLib, "Build static library for C bindings":
|
||||
exec "nim c --app:staticLib --out:../bindings/c-bindings/libchatsdk.a src/chat_sdk.nim"
|
||||
|
||||
7
examples/go-app/go.mod
Normal file
7
examples/go-app/go.mod
Normal file
@ -0,0 +1,7 @@
|
||||
module go-app
|
||||
|
||||
replace github.com/waku-org/nim-chat-sdk/bindings/go-bindings => ../../bindings/go-bindings
|
||||
|
||||
require github.com/waku-org/nim-chat-sdk/bindings/go-bindings v0.0.0-00010101000000-000000000000
|
||||
|
||||
go 1.24
|
||||
137
examples/go-app/main.go
Normal file
137
examples/go-app/main.go
Normal file
@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
chatsdk "github.com/waku-org/nim-chat-sdk/bindings/go-bindings"
|
||||
)
|
||||
|
||||
// SimpleStore implements the chatsdk.Store interface with in-memory storage
|
||||
type SimpleStore struct {
|
||||
messages map[string]string
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewSimpleStore creates a new SimpleStore instance
|
||||
func NewSimpleStore() *SimpleStore {
|
||||
return &SimpleStore{
|
||||
messages: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// StoreMessage stores a message with the given ID
|
||||
func (s *SimpleStore) StoreMessage(id, message string) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
fmt.Printf("📦 Storing message [%s]: %s\n", id, message)
|
||||
s.messages[id] = message
|
||||
return true
|
||||
}
|
||||
|
||||
// GetMessage retrieves a message by ID
|
||||
func (s *SimpleStore) GetMessage(id string) string {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
message, exists := s.messages[id]
|
||||
if exists {
|
||||
fmt.Printf("📤 Retrieved message [%s]: %s\n", id, message)
|
||||
return message
|
||||
}
|
||||
fmt.Printf("❌ Message not found [%s]\n", id)
|
||||
return ""
|
||||
}
|
||||
|
||||
// ListAllMessages shows all stored messages
|
||||
func (s *SimpleStore) ListAllMessages() {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
fmt.Println("\n📋 All stored messages:")
|
||||
if len(s.messages) == 0 {
|
||||
fmt.Println(" (no messages stored)")
|
||||
return
|
||||
}
|
||||
|
||||
for id, message := range s.messages {
|
||||
fmt.Printf(" [%s]: %s\n", id, message)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("ChatSDK Go Example - Enhanced with Storage")
|
||||
fmt.Println("==========================================")
|
||||
|
||||
// Test 1: Original standalone API (backward compatibility)
|
||||
fmt.Println("\n🔸 Testing standalone API (backward compatibility):")
|
||||
messages := []string{
|
||||
"Hello from standalone API!",
|
||||
"This message won't be stored",
|
||||
}
|
||||
|
||||
for i, msg := range messages {
|
||||
fmt.Printf("Sending standalone message #%d: %s\n", i+1, msg)
|
||||
if err := chatsdk.SendMessage(msg); err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
} else {
|
||||
fmt.Println("✓ Message sent successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// Test 2: ChatSDK object with Store interface
|
||||
fmt.Println("\n🔸 Testing ChatSDK object with Store interface:")
|
||||
|
||||
// Create a store implementation
|
||||
store := NewSimpleStore()
|
||||
|
||||
// Create ChatSDK instance with the store
|
||||
sdk, err := chatsdk.NewChatSDK(store)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create ChatSDK: %v", err)
|
||||
}
|
||||
defer sdk.Close()
|
||||
|
||||
// Send messages with IDs (they will be stored)
|
||||
testMessages := map[string]string{
|
||||
"msg1": "Hello from ChatSDK object!",
|
||||
"msg2": "This message will be stored and can be retrieved",
|
||||
"msg3": "Nim ❤️ Go with storage interface working!",
|
||||
"msg4": "Another stored message with a longer ID",
|
||||
}
|
||||
|
||||
fmt.Println("\n📤 Sending messages with storage:")
|
||||
for id, message := range testMessages {
|
||||
fmt.Printf("Sending message [%s]: %s\n", id, message)
|
||||
if err := sdk.SendMessage(id, message); err != nil {
|
||||
log.Printf("Error sending message: %v", err)
|
||||
} else {
|
||||
fmt.Println("✓ Message sent and stored successfully")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Test message retrieval
|
||||
fmt.Println("\n📥 Testing message retrieval:")
|
||||
testIDs := []string{"msg1", "msg2", "msg3", "msg4", "nonexistent"}
|
||||
|
||||
for _, id := range testIDs {
|
||||
fmt.Printf("Retrieving message [%s]...\n", id)
|
||||
message, err := sdk.GetMessage(id)
|
||||
if err != nil {
|
||||
log.Printf("Error retrieving message: %v", err)
|
||||
} else if message != "" {
|
||||
fmt.Printf("✓ Found: %s\n", message)
|
||||
} else {
|
||||
fmt.Printf("❌ Message not found\n")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Show all stored messages
|
||||
store.ListAllMessages()
|
||||
|
||||
fmt.Println("\n✅ Example completed successfully!")
|
||||
}
|
||||
108
src/chat_sdk.nim
108
src/chat_sdk.nim
@ -1,3 +1,111 @@
|
||||
import std/[times]
|
||||
|
||||
# Storage interface function pointer types
|
||||
type
|
||||
StoreMessageProc* = proc(id: cstring, message: cstring, userData: pointer): cint {.cdecl.}
|
||||
GetMessageProc* = proc(id: cstring, userData: pointer): cstring {.cdecl.}
|
||||
|
||||
# ChatSDK object
|
||||
type
|
||||
ChatSDK* = object
|
||||
storeCallback: StoreMessageProc
|
||||
getCallback: GetMessageProc
|
||||
userData: pointer # For Go-side data if needed
|
||||
|
||||
# Create a new ChatSDK instance
|
||||
proc newChatSDK*(storeProc: StoreMessageProc, getProc: GetMessageProc, userData: pointer = nil): ChatSDK =
|
||||
ChatSDK(
|
||||
storeCallback: storeProc,
|
||||
getCallback: getProc,
|
||||
userData: userData
|
||||
)
|
||||
|
||||
# Send message method for ChatSDK
|
||||
proc sendMessage*(sdk: ChatSDK, id: string, message: string): bool =
|
||||
## Sends a message by printing it to stdout with timestamp and storing it
|
||||
let timestamp = now()
|
||||
echo "[", timestamp.format("yyyy-MM-dd HH:mm:ss"), "] ChatSDK: ", message
|
||||
|
||||
# Store the message using the provided storage interface
|
||||
if sdk.storeCallback != nil:
|
||||
let storeResult = sdk.storeCallback(cstring(id), cstring(message), sdk.userData)
|
||||
return storeResult == 0
|
||||
return false
|
||||
|
||||
# Get message method for ChatSDK
|
||||
proc getMessage*(sdk: ChatSDK, id: string): string =
|
||||
## Gets a message using the provided storage interface
|
||||
if sdk.getCallback != nil:
|
||||
let messageResult = sdk.getCallback(cstring(id), sdk.userData)
|
||||
if messageResult != nil:
|
||||
return $messageResult
|
||||
return ""
|
||||
|
||||
# Original standalone sendMessage for backward compatibility
|
||||
proc sendMessage*(message: string) =
|
||||
## Sends a message by printing it to stdout with timestamp
|
||||
let timestamp = now()
|
||||
echo "[", timestamp.format("yyyy-MM-dd HH:mm:ss"), "] ChatSDK: ", message
|
||||
|
||||
# C-compatible wrappers
|
||||
proc sendMessageCString*(message: cstring): cint {.exportc, dynlib.} =
|
||||
## C-compatible wrapper for standalone sendMessage
|
||||
try:
|
||||
sendMessage($message)
|
||||
return 0 # Success
|
||||
except:
|
||||
return 1 # Error
|
||||
|
||||
proc newChatSDKC*(storeProc: StoreMessageProc, getProc: GetMessageProc, userData: pointer = nil): ptr ChatSDK {.exportc, dynlib.} =
|
||||
## C-compatible wrapper to create a new ChatSDK instance
|
||||
try:
|
||||
let sdk = newChatSDK(storeProc, getProc, userData)
|
||||
let sdkPtr = cast[ptr ChatSDK](alloc(sizeof(ChatSDK)))
|
||||
sdkPtr[] = sdk
|
||||
return sdkPtr
|
||||
except:
|
||||
return nil
|
||||
|
||||
proc freeChatSDKC*(sdk: ptr ChatSDK) {.exportc, dynlib.} =
|
||||
## C-compatible wrapper to free ChatSDK instance
|
||||
if sdk != nil:
|
||||
dealloc(sdk)
|
||||
|
||||
proc sendMessageSDKC*(sdk: ptr ChatSDK, id: cstring, message: cstring): cint {.exportc, dynlib.} =
|
||||
## C-compatible wrapper for ChatSDK sendMessage
|
||||
try:
|
||||
if sdk == nil:
|
||||
return 1
|
||||
let success = sdk[].sendMessage($id, $message)
|
||||
return if success: 0 else: 1
|
||||
except:
|
||||
return 1
|
||||
|
||||
proc getMessageSDKC*(sdk: ptr ChatSDK, id: cstring): cstring {.exportc, dynlib.} =
|
||||
## C-compatible wrapper for ChatSDK getMessage
|
||||
try:
|
||||
if sdk == nil:
|
||||
return nil
|
||||
let message = sdk[].getMessage($id) # Convert cstring to string for internal method
|
||||
if message.len > 0:
|
||||
# Allocate C string - caller must free
|
||||
let cStr = cast[cstring](alloc(message.len + 1))
|
||||
copyMem(cStr, cstring(message), message.len + 1)
|
||||
return cStr
|
||||
return nil
|
||||
except:
|
||||
return nil
|
||||
|
||||
proc freeCString*(str: cstring) {.exportc, dynlib.} =
|
||||
## Free a C string allocated by the library
|
||||
if str != nil:
|
||||
dealloc(str)
|
||||
|
||||
# Export the module for C bindings
|
||||
when isMainModule:
|
||||
sendMessage("Test message from Nim!")
|
||||
|
||||
|
||||
# This is just an example to get you started. A typical library package
|
||||
# exports the main API in this file. Note that you cannot rename this file
|
||||
# but you can remove it if you wish.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user