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}