Skip to main content

ri/c/
queue.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Queue Module C API
19//!
20//! This module provides C language bindings for Ri's message queue infrastructure. The queue module
21//! delivers high-performance asynchronous message processing with reliable delivery guarantees, multiple
22//! queue semantics, and comprehensive routing capabilities. This C API enables C/C++ applications to
23//! leverage Ri's messaging functionality for building event-driven architectures, task distribution
24//! systems, and distributed processing pipelines.
25//!
26//! ## Module Architecture
27//!
28//! The queue module comprises three primary components that together provide complete messaging
29//! functionality:
30//!
31//! - **RiQueueConfig**: Configuration container for queue parameters including queue type selection,
32//!   delivery guarantees, persistence settings, and consumer group configuration. The configuration object
33//!   controls queue behavior, resource allocation, and operational characteristics.
34//!
35//! - **RiQueueManager**: Central manager for queue lifecycle, message routing, and subscription
36//!   management. The manager handles the complete messaging workflow including message production,
37//!   consumption, acknowledgment, and dead-letter handling.
38//!
39//! - **RiQueueMessage**: Message abstraction representing individual messages in the queue system.
40//!   Messages encapsulate payload data, metadata, headers, delivery properties, and routing information.
41//!
42//! ## Queue Types
43//!
44//! The queue system supports multiple queue semantics for different use cases:
45//!
46//! - **FIFO (First-In-First-Out) Queues**: Standard message ordering where messages are delivered
47//!   in the exact order they were produced. Essential for sequential processing requirements.
48//!
49//! - **Priority Queues**: Messages are delivered based on priority levels rather than arrival order.
50//!   High-priority messages skip ahead of lower-priority messages in the delivery sequence.
51//!
52//! - **Work Queues (Task Queues)**: Multiple workers compete for messages, with each message processed
53//!   by exactly one worker. Enables horizontal scaling of processing capacity.
54//!
55//! - **Publish-Subscribe Queues**: One message published to the queue is delivered to all subscribed
56//!   consumers. Enables broadcast patterns for event distribution.
57//!
58//! - **Delay Queues**: Messages are held invisible for a configurable delay period before becoming
59//!   available for consumption. Useful for retry logic and scheduled processing.
60//!
61//! - **Dead Letter Queues**: Messages that fail processing after multiple attempts are moved to
62//!   a separate queue for later inspection and manual handling.
63//!
64//! ## Delivery Guarantees
65//!
66//! The messaging system provides configurable delivery semantics:
67//!
68//! - **At-Most-Once Delivery**: Messages are delivered zero or one time. No duplication possible,
69//!   but messages may be lost. Highest performance, lowest reliability.
70//!
71//! - **At-Least-Once Delivery**: Messages are guaranteed to be delivered at least once. Duplicates
72//!   possible, but no messages lost. Requires idempotent message handlers.
73//!
74//! - **Exactly-Once Delivery**: Messages are delivered exactly one time. No duplication, no loss.
75//!   Most complex and highest overhead. Achieved through deduplication and coordination.
76//!
77//! - **Transactional Delivery**: Messages are produced and consumed within database transactions.
78//!   Ensures atomicity across message and data operations.
79//!
80//! ## Message Properties
81//!
82//! Each message carries comprehensive metadata:
83//!
84//! - **Payload**: The actual message content, stored as bytes. Can be JSON, binary, protobuf,
85//!   or any custom format the application requires.
86//!
87//! - **Message ID**: Unique identifier for deduplication and tracking. Generated by the system
88//!   or optionally specified by the producer.
89//!
90//! - **Correlation ID**: Application-defined identifier for relating messages to each other.
91//!   Useful for request-response correlation and tracing.
92//!
93//! - **Timestamp**: When the message was published. Used for ordering and TTL calculations.
94//!
95//! - **Priority**: Message priority level (if supported by queue type). Affects delivery order.
96//!
97//! - **Delay**: Configurable delay before message becomes visible. Supports retry and scheduling.
98//!
99//! - **TTL (Time-To-Live)**: Maximum time message can remain in queue. Expired messages are
100//!   removed or moved to dead letter queue.
101//!
102//! - **Headers**: Key-value metadata pairs for routing and processing hints. Similar to HTTP
103//!   headers in purpose.
104//!
105//! ## Consumer Groups
106//!
107//! The queue system supports sophisticated consumer patterns:
108//!
109//! - **Shared Consumption**: Multiple consumers share messages from a queue, each message processed
110//!   by one consumer. Enables load balancing across consumers.
111//!
112//! - **Exclusive Consumption**: One consumer receives all messages from a queue. Other consumers
113//!   are blocked. Useful when ordering or stateful processing is required.
114//!
115//! - **Fan-Out**: Messages are replicated to multiple queues for independent consumption.
116//!   Enables parallel processing pipelines from a single source.
117//!
118//! - **Consumer Lag Tracking**: Monitor how far behind consumers are from the message production
119//!   rate. Used for capacity planning and alerting.
120//!
121//! ## Acknowledgment Patterns
122//!
123//! Message acknowledgment controls delivery guarantees:
124//!
125//! - **Auto-Acknowledge**: Messages are considered delivered immediately upon receipt. Simplest
126//!   pattern but risks message loss on consumer failure.
127//!
128//! - **Manual Acknowledge**: Consumer explicitly acknowledges successful processing. Messages
129//!   are only removed after successful ack. Supports reliable processing.
130//!
131//! - **Negative Acknowledge (Nack)**: Consumer signals processing failure, returning message
132//!   to the queue for redelivery. Can optionally increase retry count.
133//!
134//! - **Multiple Acknowledge**: Batch multiple messages with a single acknowledgment call.
135//!   Improves throughput for high-volume scenarios.
136//!
137//! ## Reliability Features
138//! The messaging system implements comprehensive reliability mechanisms:
139//!
140//! - **Message Persistence**: Messages written to durable storage survive broker restarts.
141//!   Configurable durability levels balance performance and reliability.
142//!
143//! - **Replication**: Messages copied to multiple brokers for fault tolerance. Configurable
144//!   replication factor determines fault tolerance level.
145//!
146//! - **Checkpointing**: Consumers periodically checkpoint their progress. On restart, consumers
147//!   resume from the last checkpoint rather than the beginning.
148//!
149//! - **Idempotent Producers**: Duplicate message detection using sequence numbers and deduplication
150//!   windows. Ensures exactly-once semantics despite retries.
151//!
152//! ## Performance Characteristics
153//!
154//! Queue operations are optimized for high throughput:
155//!
156//! - **Message Production**: O(1) for single message, O(n) for batching
157//! - **Message Consumption**: O(1) for retrieval with proper indexing
158//! - **Queue Creation**: O(1) for standard queues
159//! - **Throughput**: Millions of messages per second on modern hardware
160//! - **Latency**: Sub-millisecond end-to-end latency for local queues
161//!
162//! ## Memory Management
163//!
164//! All C API objects use opaque pointers with manual memory management:
165//!
166//! - Constructor functions allocate new instances on the heap
167//! - Destructor functions must be called to release memory
168//! - Message payloads must be freed appropriately
169//! - Queue managers coordinate resource cleanup
170//!
171//! ## Thread Safety
172//!
173//! The underlying implementations are thread-safe:
174//!
175//! - Concurrent message production from multiple threads supported
176//! - Multiple consumers can process messages concurrently
177//! - Queue operations use internal synchronization
178//! - Message handling should be idempotent for concurrent processing
179//!
180//! ## Usage Example
181//!
182//! ```c
183//! // Create queue configuration
184//! RiQueueConfig* config = ri_queue_config_new();
185//! if (config == NULL) {
186//!     fprintf(stderr, "Failed to create queue config\n");
187//!     return ERROR_INIT;
188//! }
189//!
190//! // Configure queue settings
191//! ri_queue_config_set_queue_type(config, QUEUE_TYPE_FIFO);
192//! ri_queue_config_set_delivery_guarantee(config, DELIVERY_AT_LEAST_ONCE);
193//! ri_queue_config_set_persistence_enabled(config, true);
194//! ri_queue_config_set_consumer_count(config, 4);
195//!
196//! // Create queue manager
197//! RiQueueManager* manager = ri_queue_manager_new(config);
198//! if (manager == NULL) {
199//!     fprintf(stderr, "Failed to create queue manager\n");
200//!     ri_queue_config_free(config);
201//!     return ERROR_INIT;
202//! }
203//!
204//! // Create a message
205//! RiQueueMessage* message = ri_queue_message_new();
206//! if (message == NULL) {
207//!     fprintf(stderr, "Failed to create message\n");
208//!     ri_queue_manager_free(manager);
209//!     ri_queue_config_free(config);
210//!     return ERROR_INIT;
211//! }
212//!
213//! // Configure message
214//! const char* payload = "{\"event\": \"user_login\", \"user_id\": 12345}";
215//! ri_queue_message_set_payload(message, payload, strlen(payload));
216//! ri_queue_message_set_correlation_id(message, "login-2024-001");
217//! ri_queue_message_set_priority(message, 5);
218//!
219//! // Set headers
220//! ri_queue_message_set_header(message, "source", "auth-service");
221//! ri_queue_message_set_header(message, "version", "1.0");
222//!
223//! // Publish message to queue
224//! int result = ri_queue_manager_publish(manager, "user-events", message);
225//! if (result != 0) {
226//!     fprintf(stderr, "Failed to publish message: %d\n", result);
227//!     ri_queue_message_free(message);
228//!     ri_queue_manager_free(manager);
229//!     ri_queue_config_free(config);
230//!     return ERROR_PUBLISH;
231//! }
232//!
233//! printf("Message published successfully\n");
234//!
235//! // Consume messages (blocking)
236//! RiQueueMessage* consumed = NULL;
237//! result = ri_queue_manager_consume(manager, "user-events", &consumed, 10000);
238//!
239//! if (result == 0 && consumed != NULL) {
240//!     // Process message
241//!     const char* received_payload = ri_queue_message_get_payload(consumed);
242//!     size_t payload_size = ri_queue_message_get_payload_size(consumed);
243//!
244//!     printf("Received: %.*s\n", (int)payload_size, received_payload);
245//!
246//!     // Get message metadata
247//!     const char* msg_id = ri_queue_message_get_id(consumed);
248//!     const char* corr_id = ri_queue_message_get_correlation_id(consumed);
249//!     uint64_t timestamp = ri_queue_message_get_timestamp(consumed);
250//!
251//!     // Process message...
252//!
253//!     // Acknowledge successful processing
254//!     ri_queue_manager_ack(manager, consumed);
255//!
256//!     ri_queue_message_free(consumed);
257//! } else if (result == TIMEOUT) {
258//!     printf("No messages available within timeout\n");
259//! } else {
260//!     fprintf(stderr, "Consume error: %d\n", result);
261//! }
262//!
263//! // Subscribe to a queue for continuous consumption
264//! RiConsumerHandle* consumer = ri_queue_manager_subscribe(
265//!     manager,
266//!     "user-events",
267//!     message_handler_callback,
268//!     NULL  // user data
269//! );
270//!
271//! if (consumer != NULL) {
272//!     // Consumer runs in background
273//!     printf("Consumer started, processing messages...\n");
274//!
275//!     // Application continues running...
276//!
277//!     // Stop consumer when done
278//!     ri_queue_manager_unsubscribe(consumer);
279//! }
280//!
281//! // Cleanup
282//! ri_queue_message_free(message);
283//! ri_queue_manager_free(manager);
284//! ri_queue_config_free(config);
285//! ```
286//!
287//! ## Message Handler Callback
288//!
289//! Message handlers must conform to the following signature:
290//!
291//! ```c
292//! typedef int (*DMSQueueMessageHandler)(
293//!     RiQueueManager* manager,
294//!     RiQueueMessage* message,
295//!     void* user_data
296//! );
297//! ```
298//!
299//! Return values:
300//!
301//! - 0: Success, message will be acknowledged
302//! - Positive: Success with value, message acknowledged
303//! - Negative: Error, message will be nacked and retried
304//!
305//! ## Dependencies
306//!
307//! This module depends on the following Ri components:
308//!
309//! - `crate::queue`: Rust queue module implementation
310//! - `crate::prelude`: Common types and traits
311//! - Async runtime for non-blocking operations
312//!
313//! ## Feature Flags
314//!
315//! The queue module is enabled by the "queue" feature flag.
316//! Disable this feature to reduce binary size when messaging is not required.
317//!
318//! Additional features:
319//!
320//! - `queue-persistence`: Enable message persistence to disk
321//! - `queue-rabbitmq`: Enable RabbitMQ backend support
322//! - `queue-kafka`: Enable Apache Kafka backend support
323//! - `queue-sqs`: Enable AWS SQS backend support
324
325use crate::queue::{RiQueueConfig, RiQueueManager, RiQueueMessage, RiQueueStats};
326use std::ffi::{c_char, c_int};
327use std::sync::Arc;
328
329c_wrapper!(CRiQueueConfig, RiQueueConfig);
330c_wrapper!(CRiQueueManager, RiQueueManager);
331
332c_constructor!(
333    ri_queue_config_new,
334    CRiQueueConfig,
335    RiQueueConfig,
336    RiQueueConfig::default()
337);
338c_destructor!(ri_queue_config_free, CRiQueueConfig);
339
340#[repr(C)]
341pub struct CRiQueueMessage {
342    pub id: *mut c_char,
343    pub payload: *mut u8,
344    pub payload_len: usize,
345    pub timestamp: u64,
346    pub retry_count: u32,
347    pub max_retries: u32,
348}
349
350#[no_mangle]
351pub extern "C" fn ri_queue_message_new(payload: *const c_char, payload_len: usize) -> *mut CRiQueueMessage {
352    if payload.is_null() || payload_len == 0 {
353        return std::ptr::null_mut();
354    }
355
356    unsafe {
357        let payload_slice = std::slice::from_raw_parts(payload as *const u8, payload_len);
358        let message = RiQueueMessage::new(payload_slice.to_vec());
359
360        let id = match std::ffi::CString::new(message.id.clone()) {
361            Ok(s) => s.into_raw(),
362            Err(_) => return std::ptr::null_mut(),
363        };
364
365        // Transfer the buffer ownership to C without invoking the Vec destructor.
366        // Equivalent of Vec::into_raw_parts() on nightly, expressed with the
367        // stable surface (as_mut_ptr + mem::forget). The C side is responsible
368        // for releasing the buffer using the same global allocator.
369        let mut payload_vec = message.payload.clone();
370        let payload_ptr = payload_vec.as_mut_ptr();
371        let payload_len_val = payload_vec.len();
372        let _payload_cap = payload_vec.capacity();
373        std::mem::forget(payload_vec);
374
375        let boxed_msg = Box::new(CRiQueueMessage {
376            id,
377            payload: payload_ptr,
378            payload_len: payload_len_val,
379            timestamp: 0,
380            retry_count: message.retry_count,
381            max_retries: message.max_retries,
382        });
383        let ptr = Box::into_raw(boxed_msg);
384        crate::c::register_ptr(ptr as usize);
385        ptr
386    }
387}
388
389#[no_mangle]
390pub extern "C" fn ri_queue_message_free(msg: *mut CRiQueueMessage) {
391    if msg.is_null() {
392        return;
393    }
394
395    if !crate::c::unregister_ptr(msg as usize) {
396        log::warn!(
397            "[Ri.C] Attempted to free unregistered or already freed queue message: {:?}",
398            msg
399        );
400        return;
401    }
402
403    unsafe {
404        let msg = Box::from_raw(msg);
405        if !msg.id.is_null() {
406            let _ = std::ffi::CString::from_raw(msg.id);
407        }
408        if !msg.payload.is_null() && msg.payload_len > 0 {
409            let _ = Vec::from_raw_parts(msg.payload, msg.payload_len, msg.payload_len);
410        }
411    }
412}
413
414#[no_mangle]
415pub extern "C" fn ri_queue_message_get_id(msg: *const CRiQueueMessage) -> *const c_char {
416    if msg.is_null() {
417        return std::ptr::null();
418    }
419    unsafe { (*msg).id }
420}
421
422#[no_mangle]
423pub extern "C" fn ri_queue_message_get_payload(msg: *const CRiQueueMessage, out_len: *mut usize) -> *const u8 {
424    if msg.is_null() || out_len.is_null() {
425        return std::ptr::null();
426    }
427    unsafe {
428        *out_len = (*msg).payload_len;
429        (*msg).payload
430    }
431}
432
433#[no_mangle]
434pub extern "C" fn ri_queue_manager_new() -> *mut CRiQueueManager {
435    let manager = RiQueueManager::default();
436    let ptr = Box::into_raw(Box::new(CRiQueueManager::new(manager)));
437    crate::c::register_ptr(ptr as usize);
438    ptr
439}
440
441#[no_mangle]
442pub extern "C" fn ri_queue_manager_free(manager: *mut CRiQueueManager) {
443    if manager.is_null() {
444        return;
445    }
446
447    if !crate::c::unregister_ptr(manager as usize) {
448        log::warn!(
449            "[Ri.C] Attempted to free unregistered or already freed queue manager: {:?}",
450            manager
451        );
452        return;
453    }
454
455    unsafe {
456        let _ = Box::from_raw(manager);
457    }
458}
459
460#[no_mangle]
461pub extern "C" fn ri_queue_manager_publish(
462    manager: *mut CRiQueueManager,
463    queue_name: *const c_char,
464    payload: *const c_char,
465    payload_len: usize,
466) -> c_int {
467    if manager.is_null() || queue_name.is_null() || payload.is_null() {
468        return -1;
469    }
470
471    unsafe {
472        let queue_str = match std::ffi::CStr::from_ptr(queue_name).to_str() {
473            Ok(s) => s,
474            Err(_) => return -2,
475        };
476
477        let payload_slice = std::slice::from_raw_parts(payload as *const u8, payload_len);
478        let message = RiQueueMessage::new(payload_slice.to_vec());
479
480        let rt = match tokio::runtime::Runtime::new() {
481            Ok(r) => r,
482            Err(_) => return -3,
483        };
484
485        let result: crate::core::RiResult<Arc<dyn crate::queue::RiQueue>> = rt.block_on(async {
486            (*manager).inner.create_queue(queue_str).await
487        });
488
489        match result {
490            Ok(queue) => {
491                let producer_result: crate::core::RiResult<Box<dyn crate::queue::RiQueueProducer>> = rt.block_on(async {
492                    queue.create_producer().await
493                });
494
495                match producer_result {
496                    Ok(producer) => {
497                        let send_result: crate::core::RiResult<()> = rt.block_on(async {
498                            producer.send(message).await
499                        });
500                        match send_result {
501                            Ok(()) => 0,
502                            Err(_) => -6,
503                        }
504                    }
505                    Err(_) => -5,
506                }
507            }
508            Err(_) => -4,
509        }
510    }
511}
512
513#[no_mangle]
514pub extern "C" fn ri_queue_manager_consume(
515    manager: *mut CRiQueueManager,
516    queue_name: *const c_char,
517    out_msg: *mut *mut CRiQueueMessage,
518    _timeout_ms: u64,
519) -> c_int {
520    if manager.is_null() || queue_name.is_null() || out_msg.is_null() {
521        return -1;
522    }
523
524    unsafe {
525        let queue_str = match std::ffi::CStr::from_ptr(queue_name).to_str() {
526            Ok(s) => s,
527            Err(_) => return -2,
528        };
529
530        let rt = match tokio::runtime::Runtime::new() {
531            Ok(r) => r,
532            Err(_) => return -3,
533        };
534
535        let queue_result: Option<Arc<dyn crate::queue::RiQueue>> = rt.block_on(async {
536            (*manager).inner.get_queue(queue_str).await
537        });
538
539        match queue_result {
540            Some(queue) => {
541                let consumer_result: crate::core::RiResult<Box<dyn crate::queue::RiQueueConsumer>> = rt.block_on(async {
542                    queue.create_consumer("default_consumer").await
543                });
544
545                match consumer_result {
546                    Ok(consumer) => {
547                        let receive_result: crate::core::RiResult<Option<RiQueueMessage>> = rt.block_on(async {
548                            consumer.receive().await
549                        });
550
551                        match receive_result {
552                            Ok(Some(msg)) => {
553                                let id = match std::ffi::CString::new(msg.id.clone()) {
554                                    Ok(s) => s.into_raw(),
555                                    Err(_) => return -7,
556                                };
557
558                                // Transfer the buffer ownership to C without invoking the Vec
559                                // destructor. Stable equivalent of Vec::into_raw_parts()
560                                // (as_mut_ptr + mem::forget). The C side releases the
561                                // buffer through the same global allocator.
562                                let mut payload_vec = msg.payload.clone();
563                                let payload_ptr = payload_vec.as_mut_ptr();
564                                let payload_len_val = payload_vec.len();
565                                let _payload_cap = payload_vec.capacity();
566                                std::mem::forget(payload_vec);
567
568                                *out_msg = Box::into_raw(Box::new(CRiQueueMessage {
569                                    id,
570                                    payload: payload_ptr,
571                                    payload_len: payload_len_val,
572                                    timestamp: 0,
573                                    retry_count: msg.retry_count,
574                                    max_retries: msg.max_retries,
575                                }));
576                                0
577                            }
578                            Ok(None) => 1,
579                            Err(_) => -6,
580                        }
581                    }
582                    Err(_) => -5,
583                }
584            }
585            None => -4,
586        }
587    }
588}
589
590#[repr(C)]
591pub struct CRiQueueStats {
592    pub queue_name: *mut c_char,
593    pub message_count: u64,
594    pub consumer_count: u32,
595    pub producer_count: u32,
596    pub processed_messages: u64,
597    pub failed_messages: u64,
598    pub avg_processing_time_ms: f64,
599    pub total_bytes_sent: u64,
600    pub total_bytes_received: u64,
601    pub last_message_time: u64,
602}
603
604#[no_mangle]
605pub extern "C" fn ri_queue_manager_stats(
606    manager: *mut CRiQueueManager,
607    queue_name: *const c_char,
608    out_stats: *mut CRiQueueStats,
609) -> c_int {
610    if manager.is_null() || queue_name.is_null() || out_stats.is_null() {
611        return -1;
612    }
613
614    unsafe {
615        let queue_str = match std::ffi::CStr::from_ptr(queue_name).to_str() {
616            Ok(s) => s,
617            Err(_) => return -2,
618        };
619
620        let rt = match tokio::runtime::Runtime::new() {
621            Ok(r) => r,
622            Err(_) => return -3,
623        };
624
625        let queue_result: Option<Arc<dyn crate::queue::RiQueue>> = rt.block_on(async {
626            (*manager).inner.get_queue(queue_str).await
627        });
628
629        match queue_result {
630            Some(queue) => {
631                let stats_result: crate::core::RiResult<RiQueueStats> = rt.block_on(async {
632                    queue.get_stats().await
633                });
634
635                match stats_result {
636                    Ok(stats) => {
637                        let queue_name = match std::ffi::CString::new(stats.queue_name.clone()) {
638                            Ok(s) => s.into_raw(),
639                            Err(_) => return -5,
640                        };
641
642                        *out_stats = CRiQueueStats {
643                            queue_name,
644                            message_count: stats.message_count,
645                            consumer_count: stats.consumer_count,
646                            producer_count: stats.producer_count,
647                            processed_messages: stats.processed_messages,
648                            failed_messages: stats.failed_messages,
649                            avg_processing_time_ms: stats.avg_processing_time_ms,
650                            total_bytes_sent: stats.total_bytes_sent,
651                            total_bytes_received: stats.total_bytes_received,
652                            last_message_time: stats.last_message_time,
653                        };
654                        0
655                    }
656                    Err(_) => -4,
657                }
658            }
659            None => -4,
660        }
661    }
662}
663
664#[no_mangle]
665pub extern "C" fn ri_queue_stats_free(stats: *mut CRiQueueStats) {
666    if stats.is_null() {
667        return;
668    }
669
670    unsafe {
671        let stats = Box::from_raw(stats);
672        if !stats.queue_name.is_null() {
673            let _ = std::ffi::CString::from_raw(stats.queue_name);
674        }
675    }
676}
677
678#[no_mangle]
679pub extern "C" fn ri_queue_manager_ack(
680    manager: *mut CRiQueueManager,
681    queue_name: *const c_char,
682    message_id: *const c_char,
683) -> c_int {
684    if manager.is_null() || queue_name.is_null() || message_id.is_null() {
685        return -1;
686    }
687
688    unsafe {
689        let queue_str = match std::ffi::CStr::from_ptr(queue_name).to_str() {
690            Ok(s) => s,
691            Err(_) => return -2,
692        };
693
694        let msg_id = match std::ffi::CStr::from_ptr(message_id).to_str() {
695            Ok(s) => s,
696            Err(_) => return -3,
697        };
698
699        let rt = match tokio::runtime::Runtime::new() {
700            Ok(r) => r,
701            Err(_) => return -4,
702        };
703
704        let queue_result: Option<Arc<dyn crate::queue::RiQueue>> = rt.block_on(async {
705            (*manager).inner.get_queue(queue_str).await
706        });
707
708        match queue_result {
709            Some(queue) => {
710                let consumer_result: crate::core::RiResult<Box<dyn crate::queue::RiQueueConsumer>> = rt.block_on(async {
711                    queue.create_consumer("default_consumer").await
712                });
713
714                match consumer_result {
715                    Ok(consumer) => {
716                        let ack_result: crate::core::RiResult<()> = rt.block_on(async {
717                            consumer.ack(msg_id).await
718                        });
719                        match ack_result {
720                            Ok(()) => 0,
721                            Err(_) => -7,
722                        }
723                    }
724                    Err(_) => -6,
725                }
726            }
727            None => -5,
728        }
729    }
730}
731
732#[no_mangle]
733pub extern "C" fn ri_queue_manager_nack(
734    manager: *mut CRiQueueManager,
735    queue_name: *const c_char,
736    message_id: *const c_char,
737) -> c_int {
738    if manager.is_null() || queue_name.is_null() || message_id.is_null() {
739        return -1;
740    }
741
742    unsafe {
743        let queue_str = match std::ffi::CStr::from_ptr(queue_name).to_str() {
744            Ok(s) => s,
745            Err(_) => return -2,
746        };
747
748        let msg_id = match std::ffi::CStr::from_ptr(message_id).to_str() {
749            Ok(s) => s,
750            Err(_) => return -3,
751        };
752
753        let rt = match tokio::runtime::Runtime::new() {
754            Ok(r) => r,
755            Err(_) => return -4,
756        };
757
758        let queue_result: Option<Arc<dyn crate::queue::RiQueue>> = rt.block_on(async {
759            (*manager).inner.get_queue(queue_str).await
760        });
761
762        match queue_result {
763            Some(queue) => {
764                let consumer_result: crate::core::RiResult<Box<dyn crate::queue::RiQueueConsumer>> = rt.block_on(async {
765                    queue.create_consumer("default_consumer").await
766                });
767
768                match consumer_result {
769                    Ok(consumer) => {
770                        let nack_result: crate::core::RiResult<()> = rt.block_on(async {
771                            consumer.nack(msg_id).await
772                        });
773                        match nack_result {
774                            Ok(()) => 0,
775                            Err(_) => -7,
776                        }
777                    }
778                    Err(_) => -6,
779                }
780            }
781            None => -5,
782        }
783    }
784}