mirror of
https://github.com/logos-messaging/nim-chat-sdk.git
synced 2026-01-04 07:03:09 +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
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 2.0.0"
|
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
|
# 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
|
# exports the main API in this file. Note that you cannot rename this file
|
||||||
# but you can remove it if you wish.
|
# but you can remove it if you wish.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user