1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
extern crate alloc;
use crate::r#yield;
use alloc::vec::Vec;
use cstr_core::{c_char, CString};

extern "C" {
    fn ipc_publish(topic: *const c_char, msg_ptr: *mut u8, msg_len: usize) -> usize;
    fn ipc_subscribe(topic: *const c_char) -> usize;
    fn ipc_unsubscribe(topic: *const c_char) -> usize;
    fn ipc_get_message(topic: *const c_char, block: bool) -> Message;
}

#[repr(C)]
pub struct Message {
    pub msg_ptr: *mut u8,
    pub msg_len: usize,
    pub valid: bool,
}

impl From<Vec<u8>> for Message {
    fn from(mut item: Vec<u8>) -> Self {
        item.shrink_to_fit();
        let (msg_ptr, msg_len, _msg_cap) = item.into_raw_parts();
        Message {
            msg_ptr,
            msg_len,
            valid: true,
        }
    }
}

impl Into<Vec<u8>> for Message {
    fn into(self) -> Vec<u8> {
        unsafe { Vec::from_raw_parts(self.msg_ptr, self.msg_len, self.msg_len) }
    }
}

/// A data structure that allows tasks to publish messages to an IPC topic
pub struct Publisher {
    pub topic: CString,
}

impl Publisher {
    /// Creates a new Publisher for the specified topic
    ///
    /// #Examples
    ///
    /// ```
    /// let my_publisher = Publisher::new("my topic").unwrap();
    /// ```
    pub fn new(topic: &str) -> Result<Self, &'static str> {
        let c_topic = match CString::new(topic) {
            Ok(t) => t,
            Err(_) => return Err("Invalid topic string"),
        };
        Ok(Publisher { topic: c_topic })
    }

    /// Publishes a message to the topic.
    ///
    /// #Examples
    /// ```
    /// my_publisher.publish("Hello, World!".into_bytes());
    /// ```
    pub fn publish(&mut self, mut message: Vec<u8>) -> Result<(), &'static str> {
        message.shrink_to_fit();
        let (msg_ptr, msg_len, _msg_cap) = message.into_raw_parts();
        let success = unsafe { ipc_publish((&self.topic).as_ptr(), msg_ptr, msg_len) };
        if success == 0 {
            r#yield();
            Ok(())
        } else {
            Err("Unable to publish to topic.")
        }
    }
}

/// A data structure that allows tasks to subscribe to a topic
pub struct Subscriber {
    pub topic: CString,
}

impl Subscriber {
    /// Creates a new Subscriber for the specified topic
    ///
    /// ```
    /// let my_subscriber = Subscriber::new("my topic").unwrap();
    /// ```
    pub fn new(topic: &str) -> Result<Self, &'static str> {
        let c_topic = match CString::new(topic) {
            Ok(t) => t,
            Err(_) => return Err("Invalid topic string"),
        };
        let new_sub = Subscriber { topic: c_topic };
        let resp = unsafe { ipc_subscribe((&new_sub.topic).as_ptr()) };
        if resp == 1 {
            Err("Failed to subscribe to topic.")
        } else {
            Ok(new_sub)
        }
    }

    fn get_message_base(&mut self, block: bool) -> Option<Vec<u8>> {
        let message: Message = unsafe { ipc_get_message((&self.topic).as_ptr(), block) };
        if message.valid {
            Some(message.into())
        } else {
            None
        }
    }

    /// Returns the next message from the topic.
    /// Blocks if there are no new messages.
    pub fn get_message(&mut self) -> Option<Vec<u8>> {
        self.get_message_base(true)
    }

    /// Returns the next message from the topic if there is one.
    /// If there are no new messages, it returns None
    pub fn get_message_nonblocking(&mut self) -> Option<Vec<u8>> {
        self.get_message_base(false)
    }
}
impl Drop for Subscriber {
    fn drop(&mut self) {
        unsafe {
            ipc_unsubscribe((&self.topic).as_ptr());
        }
    }
}