├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── blocking_receive.rs ├── channel_receive.rs ├── loopback.rs └── nonblocking_receive.rs └── src ├── async ├── client.rs ├── error.rs ├── iterator.rs ├── mod.rs └── options.rs ├── ffiasync.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mqtt" 3 | version = "0.5.0" 4 | authors = ["Andres Vahter "] 5 | 6 | [dependencies] 7 | libc = "*" 8 | log = "*" 9 | time = "*" 10 | 11 | [dev-dependencies] 12 | fern = "*" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 andres.vahter@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #rust-mqtt 2 | This library is a Rust wrapper around [paho.mqtt.c lib](https://github.com/eclipse/paho.mqtt.c). 3 | Currently only async lib is implemented. 4 | 5 | ## Dependencies 6 | 7 | Build [paho.mqtt.c lib](https://github.com/eclipse/paho.mqtt.c) from **develop** branch. 8 | 9 | cd ~/Downloads 10 | git clone https://github.com/eclipse/paho.mqtt.c.git 11 | cd paho.mqtt.c 12 | git checkout develop 13 | make 14 | sudo make install 15 | 16 | Mac OS X 17 | 18 | Rust-mqtt links to `libpaho-mqtt3a`. Mac OS X is not able find it from original files therefore create symlinks: 19 | 20 | ln -s /usr/local/lib/libpaho-mqtt3a.so.1.0 /usr/local/lib/libpaho-mqtt3a.dylib 21 | ln -s /usr/local/lib/libpaho-mqtt3a.so.1.0 /usr/local/lib/libpaho-mqtt3a.so.1 22 | 23 | ## Usage 24 | Put this in your `Cargo.toml`: 25 | 26 | ```toml 27 | [dependencies.mqtt] 28 | git = "https://github.com/cubehub/rust-mqtt.git" 29 | ``` 30 | 31 | And this in your crate root: 32 | 33 | ```rust 34 | extern crate mqtt; 35 | ``` 36 | 37 | ## Examples 38 | 39 | Install and start [mosquitto](http://mosquitto.org) broker. 40 | 41 | Mac OS X 42 | 43 | brew install mosquitto 44 | /usr/local/sbin/mosquitto 45 | 46 | Ubuntu 47 | 48 | sudo apt-get install mosquitto mosquitto-clients 49 | mosquitto 50 | 51 | Start rust-mqtt [loopback example](https://github.com/cubehub/rust-mqtt/blob/master/examples/loopback.rs): 52 | 53 | cargo run --example loopback 54 | 55 | 56 | ## For rust-mqtt developers 57 | 58 | Tool called rust-bindgen is used to generate Rust functions from C files. 59 | 60 | Mac OS X: 61 | 62 | echo export DYLD_LIBRARY_PATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/:$DYLD_LIBRARY_PATH >> ~/.profile 63 | 64 | Build [rust-bindgen](https://github.com/crabtw/rust-bindgen) 65 | 66 | git clone https://github.com/crabtw/rust-bindgen.git 67 | cd rust-bindgen 68 | cargo build 69 | 70 | Generate Rust bindings 71 | 72 | ./target/debug/bindgen -l paho-mqtt3a -match MQTTAsync.h -match MQTTClientPersistence.h -o ~/Development/rust-mqtt/src/ffimqttasync.rs ~/Downloads/paho.mqtt.c/src/MQTTAsync.h 73 | 74 | Notice that there are some issues with rust-bindgen generated code for callbacks. Therefore some manual modifications must be made to ffimqttasync.rs. Here is an example how to do it correctly: 75 | 76 | * commit: [fix ffimqttasync callbacks, rust-bindgen got them wrong](https://github.com/cubehub/rust-mqtt/commit/b3172439b11a4faff66750ece80371a90c34a0f9) 77 | -------------------------------------------------------------------------------- /examples/blocking_receive.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate fern; 4 | extern crate time; 5 | extern crate mqtt; 6 | 7 | use mqtt::async::{PersistenceType, Qos, MqttError, AsyncClient, AsyncConnectOptions, AsyncDisconnectOptions}; 8 | use std::error::Error; 9 | 10 | 11 | fn conf_logger() { 12 | let logger_config = fern::DispatchConfig { 13 | format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| { 14 | let t = time::now(); 15 | let ms = t.tm_nsec/1000_000; 16 | format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg) 17 | }), 18 | output: vec![fern::OutputConfig::stderr()], 19 | level: log::LogLevelFilter::Trace, 20 | }; 21 | 22 | if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) { 23 | panic!("Failed to initialize global logger: {}", e); 24 | } 25 | } 26 | 27 | fn setup_mqtt(server_address: &str, topic: &str, client_id: &str) -> Result { 28 | let connect_options = AsyncConnectOptions::new(); 29 | let mut client = try!(AsyncClient::new(server_address, client_id, PersistenceType::Nothing, None)); 30 | try!(client.connect(&connect_options)); 31 | try!(client.subscribe(topic, Qos::FireAndForget)); 32 | Ok(client) 33 | } 34 | 35 | fn main() { 36 | // setup fern logger 37 | conf_logger(); 38 | 39 | // start processing 40 | info!("blocking receive test started"); 41 | info!("run: mosquitto_pub -t TestTopic -m somedata to send some messages to the test"); 42 | 43 | let topic = "TestTopic"; 44 | match setup_mqtt("tcp://localhost:1883", &topic, "TestClientId") { 45 | Ok(mut client) => { 46 | 47 | // thread blocks here until message is received 48 | for message in client.messages(None) { 49 | info!("{:?}", message); 50 | } 51 | 52 | let disconnect_options = AsyncDisconnectOptions::new(); 53 | client.disconnect(&disconnect_options).unwrap(); 54 | }, 55 | Err(e) => error!("{}; raw error: {}", e.description(), e) 56 | } 57 | info!("blocking receive test ended"); 58 | } 59 | -------------------------------------------------------------------------------- /examples/channel_receive.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate fern; 4 | extern crate time; 5 | extern crate mqtt; 6 | 7 | use mqtt::async::{PersistenceType, Qos, MqttError, AsyncClient, AsyncConnectOptions, AsyncDisconnectOptions, Message}; 8 | use std::error::Error; 9 | use std::sync::mpsc; 10 | 11 | fn conf_logger() { 12 | let logger_config = fern::DispatchConfig { 13 | format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| { 14 | let t = time::now(); 15 | let ms = t.tm_nsec/1000_000; 16 | format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg) 17 | }), 18 | output: vec![fern::OutputConfig::stderr()], 19 | level: log::LogLevelFilter::Trace, 20 | }; 21 | 22 | if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) { 23 | panic!("Failed to initialize global logger: {}", e); 24 | } 25 | } 26 | 27 | fn setup_mqtt(server_address: &str, topic: &str, client_id: &str, channel: mpsc::Sender) -> Result { 28 | let connect_options = AsyncConnectOptions::new(); 29 | let mut client = try!(AsyncClient::new(server_address, client_id, PersistenceType::Nothing, Some(channel))); 30 | try!(client.connect(&connect_options)); 31 | try!(client.subscribe(topic, Qos::FireAndForget)); 32 | Ok(client) 33 | } 34 | 35 | fn main() { 36 | // setup fern logger 37 | conf_logger(); 38 | 39 | // start processing 40 | info!("channel receive test started"); 41 | info!("run: mosquitto_pub -t TestTopic -m somedata to send some messages to the test"); 42 | 43 | let (txchannel, rxchannel) = mpsc::channel::(); 44 | 45 | let topic = "TestTopic"; 46 | match setup_mqtt("tcp://localhost:1883", &topic, "TestClientId", txchannel) { 47 | Ok(mut client) => { 48 | 49 | loop { 50 | info!("wait for a message.."); 51 | let message = rxchannel.recv().unwrap(); 52 | info!("{:?}", message); 53 | } 54 | 55 | let disconnect_options = AsyncDisconnectOptions::new(); 56 | client.disconnect(&disconnect_options).unwrap(); 57 | }, 58 | Err(e) => error!("{}; raw error: {}", e.description(), e) 59 | } 60 | info!("channel receive test ended"); 61 | } 62 | -------------------------------------------------------------------------------- /examples/loopback.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate fern; 4 | extern crate time; 5 | extern crate mqtt; 6 | 7 | use std::thread; 8 | use std::char; 9 | use mqtt::async::{PersistenceType, Qos, MqttError, AsyncClient, AsyncConnectOptions, AsyncDisconnectOptions}; 10 | use std::error::Error; 11 | 12 | 13 | fn conf_logger() { 14 | let logger_config = fern::DispatchConfig { 15 | format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| { 16 | let t = time::now(); 17 | let ms = t.tm_nsec/1000_000; 18 | format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg) 19 | }), 20 | output: vec![fern::OutputConfig::stderr()], 21 | level: log::LogLevelFilter::Trace, 22 | }; 23 | 24 | if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) { 25 | panic!("Failed to initialize global logger: {}", e); 26 | } 27 | } 28 | 29 | fn setup_mqtt(server_address: &str, topic: &str, client_id: &str) -> Result { 30 | let connect_options = AsyncConnectOptions::new(); 31 | let mut client = try!(AsyncClient::new(server_address, client_id, PersistenceType::Nothing, None)); 32 | try!(client.connect(&connect_options)); 33 | try!(client.subscribe(topic, Qos::FireAndForget)); 34 | Ok(client) 35 | } 36 | 37 | fn main() { 38 | // setup fern logger 39 | conf_logger(); 40 | 41 | // start processing 42 | info!("loopback test started"); 43 | 44 | let mut data = Vec::new(); 45 | let topic = "TestTopic"; 46 | match setup_mqtt("tcp://localhost:1883", &topic, "TestClientId") { 47 | Ok(mut client) => { 48 | for i in 0..10 { 49 | info!("send data len: {}", i); 50 | data.push(char::from_digit(i % 10, 10).unwrap() as u8); 51 | client.send(&data, &topic, Qos::FireAndForget, false).unwrap(); 52 | for message in client.messages(Some(100)) { 53 | info!("{:?}", message); 54 | } 55 | thread::sleep_ms(200); 56 | } 57 | 58 | let disconnect_options = AsyncDisconnectOptions::new(); 59 | client.disconnect(&disconnect_options).unwrap(); 60 | }, 61 | Err(e) => error!("{}; raw error: {}", e.description(), e) 62 | } 63 | info!("loopback test ended"); 64 | } 65 | -------------------------------------------------------------------------------- /examples/nonblocking_receive.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate fern; 4 | extern crate time; 5 | extern crate mqtt; 6 | 7 | use mqtt::async::{PersistenceType, Qos, MqttError, AsyncClient, AsyncConnectOptions, AsyncDisconnectOptions}; 8 | use std::error::Error; 9 | 10 | 11 | fn conf_logger() { 12 | let logger_config = fern::DispatchConfig { 13 | format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| { 14 | let t = time::now(); 15 | let ms = t.tm_nsec/1000_000; 16 | format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg) 17 | }), 18 | output: vec![fern::OutputConfig::stderr()], 19 | level: log::LogLevelFilter::Trace, 20 | }; 21 | 22 | if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) { 23 | panic!("Failed to initialize global logger: {}", e); 24 | } 25 | } 26 | 27 | fn setup_mqtt(server_address: &str, topic: &str, client_id: &str) -> Result { 28 | let connect_options = AsyncConnectOptions::new(); 29 | let mut client = try!(AsyncClient::new(server_address, client_id, PersistenceType::Nothing, None)); 30 | try!(client.connect(&connect_options)); 31 | try!(client.subscribe(topic, Qos::FireAndForget)); 32 | Ok(client) 33 | } 34 | 35 | fn main() { 36 | // setup fern logger 37 | conf_logger(); 38 | 39 | // start processing 40 | info!("non-blocking receive test started"); 41 | info!("run: mosquitto_pub -t TestTopic -m somedata to send some messages to the test"); 42 | 43 | let topic = "TestTopic"; 44 | match setup_mqtt("tcp://localhost:1883", &topic, "TestClientId") { 45 | Ok(mut client) => { 46 | 47 | loop { 48 | info!("wait for a message.."); 49 | let timeout_ms = Some(500); 50 | for message in client.messages(timeout_ms) { 51 | info!("{:?}", message); 52 | } 53 | } 54 | 55 | let disconnect_options = AsyncDisconnectOptions::new(); 56 | client.disconnect(&disconnect_options).unwrap(); 57 | }, 58 | Err(e) => error!("{}; raw error: {}", e.description(), e) 59 | } 60 | info!("non-blocking receive test ended"); 61 | } 62 | -------------------------------------------------------------------------------- /src/async/client.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | use ffiasync; 26 | use libc::{c_char, c_int, c_void}; 27 | use std::ffi::{CStr, CString}; 28 | use std::mem; 29 | use std::slice; 30 | use std::sync::{Barrier, Arc, Mutex, Condvar}; 31 | 32 | use super::Message; 33 | use super::options::{PersistenceType, Qos, AsyncConnectOptions, AsyncDisconnectOptions}; 34 | use super::error::{MqttError, CommandError, ConnectError, ConnectErrReturnCode, DisconnectError, DisconnectErrReturnCode, CallbackError}; 35 | use super::iterator::AsyncClientIntoIterator; 36 | 37 | use std::sync::mpsc; 38 | 39 | pub struct AsyncClient { 40 | inner: Box 41 | } 42 | 43 | impl AsyncClient { 44 | pub fn new(address: &str, clientid: &str, persistence: PersistenceType, message_channel: Option>) -> Result { 45 | let mut ac = AsyncClient { 46 | inner: Box::new(ImmovableClient::new(address, clientid, persistence, message_channel)) 47 | }; 48 | try!(ac.inner.create()); 49 | Ok(ac) 50 | } 51 | pub fn connect(&mut self, options: &AsyncConnectOptions) -> Result<(), MqttError> { 52 | self.inner.connect(options) 53 | } 54 | pub fn disconnect(&mut self, options: &AsyncDisconnectOptions) -> Result<(), MqttError> { 55 | self.inner.disconnect(options) 56 | } 57 | pub fn is_connected(&self) -> bool { 58 | self.inner.is_connected() 59 | } 60 | pub fn send(&mut self, data: &[u8], topic: &str, qos: Qos, retained: bool) -> Result<(), MqttError> { 61 | self.inner.send(data, topic, qos, retained) 62 | } 63 | pub fn subscribe(&mut self, topic: &str, qos: Qos) -> Result<(), MqttError> { 64 | self.inner.subscribe(topic, qos) 65 | } 66 | pub fn messages(&mut self, timeout_ms: Option) -> AsyncClientIntoIterator { 67 | AsyncClientIntoIterator::new(self.inner.messages.clone(), timeout_ms) 68 | } 69 | } 70 | 71 | 72 | struct ImmovableClient { 73 | c_url : CString, 74 | c_clientid : CString, 75 | handle : ffiasync::MQTTAsync, 76 | persistence_context : c_void, 77 | persistence : PersistenceType, 78 | 79 | barrier : Barrier, 80 | action_result : Option>, 81 | pub messages : Arc<(Mutex>, Condvar)>, 82 | channel : Option>, 83 | } 84 | impl ImmovableClient { 85 | fn context(&mut self) -> *mut c_void { 86 | self as *mut _ as *mut c_void 87 | } 88 | 89 | pub fn new(address: &str, clientid: &str, persistence: PersistenceType, message_channel: Option>) -> Self { 90 | ImmovableClient { 91 | c_url : CString::new(address).unwrap(), 92 | c_clientid : CString::new(clientid).unwrap(), 93 | handle : unsafe{mem::zeroed()}, 94 | persistence_context : unsafe{mem::zeroed()}, 95 | persistence : persistence, 96 | 97 | barrier : Barrier::new(2), 98 | action_result : None, 99 | messages : Arc::new((Mutex::new(Vec::new()), Condvar::new())), 100 | channel : message_channel 101 | } 102 | } 103 | 104 | pub fn create(&mut self) -> Result<(), MqttError> { 105 | let array_url = self.c_url.as_bytes_with_nul(); 106 | let array_clientid = self.c_clientid.as_bytes_with_nul(); 107 | let error = unsafe { 108 | ffiasync::MQTTAsync_create(&mut self.handle, 109 | mem::transmute::<&u8, *const c_char>(&array_url[0]), 110 | mem::transmute::<&u8, *const c_char>(&array_clientid[0]), 111 | self.persistence as i32, 112 | &mut self.persistence_context) 113 | }; 114 | match error { 115 | 0 => { Ok(())}, 116 | err => Err(MqttError::Create(err)) 117 | } 118 | } 119 | 120 | pub fn connect(&mut self, options: &AsyncConnectOptions) -> Result<(), MqttError> { 121 | debug!("connect.."); 122 | unsafe { 123 | ffiasync::MQTTAsync_setCallbacks(self.handle, 124 | self.context(), 125 | Some(Self::disconnected), 126 | Some(Self::received), 127 | None); 128 | } 129 | 130 | let mut async_opts = ffiasync::MQTTAsync_connectOptions::new(); 131 | 132 | // fill in FFI private struct 133 | async_opts.keepAliveInterval = options.keep_alive_interval; 134 | async_opts.cleansession = options.cleansession; 135 | async_opts.maxInflight = options.max_in_flight; 136 | async_opts.connectTimeout = options.connect_timeout; 137 | async_opts.retryInterval = options.retry_interval; 138 | 139 | // register callbacks 140 | async_opts.context = self.context(); 141 | async_opts.onSuccess = Some(Self::action_succeeded); 142 | async_opts.onFailure = Some(Self::action_failed); 143 | 144 | self.action_result = None; 145 | let error = unsafe { 146 | ffiasync::MQTTAsync_connect(self.handle, &async_opts) 147 | }; 148 | if error == 0 { 149 | self.barrier.wait(); 150 | match (self.is_connected(), &self.action_result) { 151 | (true, _ ) => Ok(()), 152 | (false, &None ) => unreachable!(), // barrier should ensure we have something 153 | (false, &Some(Ok(())) ) => unreachable!(), // callback and is_connected() don't agree? 154 | (false, &Some(Err(CallbackError::Response(r)))) => Err(MqttError::Connect(ConnectError::CallbackResponse(r))), 155 | (false, &Some(Err(CallbackError::NullPtr)) ) => Err(MqttError::Connect(ConnectError::CallbackNullPtr)), 156 | } 157 | } else { Err(MqttError::Connect(ConnectError::ReturnCode(ConnectErrReturnCode::from_int(error)))) } 158 | } 159 | 160 | pub fn disconnect(&mut self, options: &AsyncDisconnectOptions) -> Result<(), MqttError> { 161 | debug!("disconnect.."); 162 | // client must be already conneced to do disconnect 163 | if !self.is_connected() { 164 | Ok(()) 165 | } 166 | else { 167 | let mut async_opts = ffiasync::MQTTAsync_disconnectOptions::new(); 168 | 169 | // fill in FFI private struct 170 | async_opts.timeout = options.timeout; 171 | 172 | // register callbacks 173 | async_opts.context = self.context(); 174 | async_opts.onSuccess = Some(Self::action_succeeded); 175 | async_opts.onFailure = Some(Self::action_failed); 176 | 177 | self.action_result = None; 178 | let error = unsafe { 179 | ffiasync::MQTTAsync_disconnect(self.handle, &async_opts) 180 | }; 181 | if error == 0 { 182 | self.barrier.wait(); 183 | match (self.is_connected(), &self.action_result) { 184 | (false, _ ) => Ok(()), 185 | (_, &None ) => unreachable!(), // barrier should ensure we have something 186 | (_, &Some(Ok(())) ) => unreachable!(), // callback and is_connected() don't agree? 187 | (_, &Some(Err(CallbackError::Response(r)))) => Err(MqttError::Disconnect(DisconnectError::CallbackResponse(r))), 188 | (_, &Some(Err(CallbackError::NullPtr)) ) => Err(MqttError::Disconnect(DisconnectError::CallbackNullPtr)), 189 | } 190 | } else { Err(MqttError::Disconnect(DisconnectError::ReturnCode(DisconnectErrReturnCode::from_int(error)))) } 191 | } 192 | } 193 | 194 | #[allow(unused_variables)] 195 | extern "C" fn disconnected(context: *mut c_void, cause: *mut c_char) -> () { 196 | warn!("disconnected"); 197 | assert!(!context.is_null()); 198 | } 199 | 200 | pub fn is_connected(&self) -> bool { 201 | let ret = unsafe { 202 | ffiasync::MQTTAsync_isConnected(self.handle) 203 | }; 204 | 205 | match ret { 206 | 1 => true, 207 | _ => false, 208 | } 209 | } 210 | 211 | pub fn send(&mut self, data: &[u8], topic: &str, qos: Qos, retained: bool) -> Result<(), MqttError> { 212 | debug!("send.."); 213 | let mut responseoption = ffiasync::MQTTAsync_responseOptions { 214 | struct_id : ['M' as i8, 'Q' as i8, 'T' as i8, 'R' as i8], 215 | struct_version : 0, 216 | onSuccess : Some(Self::action_succeeded), 217 | onFailure : Some(Self::action_failed), 218 | context : self.context(), 219 | token : 0, 220 | }; 221 | 222 | let retained = match retained { 223 | true => 1, 224 | false => 0, 225 | }; 226 | 227 | let mut message = ffiasync::MQTTAsync_message { 228 | struct_id : ['M' as i8, 'Q' as i8, 'T' as i8, 'M' as i8], 229 | struct_version : 0, 230 | payloadlen : data.len() as i32, 231 | payload : unsafe {mem::transmute::<&u8, *mut c_void>(&data[0])}, 232 | qos : qos as c_int, 233 | retained : retained as c_int, 234 | dup : 0, 235 | msgid : 0, 236 | }; 237 | 238 | let c_topic = CString::new(topic).unwrap(); 239 | let array_topic = c_topic.as_bytes_with_nul(); 240 | self.action_result = None; 241 | 242 | let error = unsafe { 243 | ffiasync::MQTTAsync_sendMessage(self.handle, 244 | mem::transmute::<&u8, *const c_char>(&array_topic[0]), 245 | &mut message, 246 | &mut responseoption) 247 | }; 248 | if error == 0 { 249 | self.barrier.wait(); 250 | match (self.is_connected(), &self.action_result) { 251 | (true, _ ) => Ok(()), 252 | (false, &None ) => unreachable!(), // barrier should ensure we have something 253 | (false, &Some(Ok(())) ) => unreachable!(), // callback and is_connected() don't agree? 254 | (false, &Some(Err(CallbackError::Response(r)))) => Err(MqttError::Send(CommandError::CallbackResponse(r))), 255 | (false, &Some(Err(CallbackError::NullPtr)) ) => Err(MqttError::Send(CommandError::CallbackNullPtr)), 256 | } 257 | } else { Err(MqttError::Send(CommandError::ReturnCode(error))) } 258 | } 259 | 260 | pub fn subscribe(&mut self, topic: &str, qos: Qos) -> Result<(), MqttError> { 261 | debug!("subscribe.."); 262 | let mut responseoption = ffiasync::MQTTAsync_responseOptions { 263 | struct_id : ['M' as i8, 'Q' as i8, 'T' as i8, 'R' as i8], 264 | struct_version : 0, 265 | onSuccess : Some(Self::action_succeeded), 266 | onFailure : Some(Self::action_failed), 267 | context : self.context(), 268 | token : 0, 269 | }; 270 | 271 | let c_topic = CString::new(topic).unwrap(); 272 | let array_topic = c_topic.as_bytes_with_nul(); 273 | 274 | let c_qos: i32 = match qos { 275 | Qos::FireAndForget => 0, 276 | Qos::AtLeastOnce => 1, 277 | Qos::OnceAndOneOnly => 2, 278 | }; 279 | self.action_result = None; 280 | 281 | let error = unsafe { 282 | ffiasync::MQTTAsync_subscribe(self.handle, 283 | mem::transmute::<&u8, *const c_char>(&array_topic[0]), 284 | c_qos, 285 | &mut responseoption) 286 | }; 287 | 288 | if error == 0 { 289 | self.barrier.wait(); 290 | match (self.is_connected(), &self.action_result) { 291 | (true, _ ) => Ok(()), 292 | (false, &None ) => unreachable!(), // barrier should ensure we have something 293 | (false, &Some(Ok(())) ) => unreachable!(), // callback and is_connected() don't agree? 294 | (false, &Some(Err(CallbackError::Response(r)))) => Err(MqttError::Subscribe(CommandError::CallbackResponse(r))), 295 | (false, &Some(Err(CallbackError::NullPtr)) ) => Err(MqttError::Subscribe(CommandError::CallbackNullPtr)), 296 | } 297 | } else { Err(MqttError::Subscribe(CommandError::ReturnCode(error))) } 298 | } 299 | 300 | #[allow(unused_variables)] 301 | extern "C" fn action_succeeded(context: *mut ::libc::c_void, response: *mut ffiasync::MQTTAsync_successData) -> () { 302 | debug!("success callback"); 303 | assert!(!context.is_null()); 304 | let selfclient: &mut ImmovableClient = unsafe {mem::transmute(context)}; 305 | selfclient.action_result = Some(Ok(())); 306 | selfclient.barrier.wait(); 307 | } 308 | 309 | extern "C" fn action_failed(context: *mut ::libc::c_void, response: *mut ffiasync::MQTTAsync_failureData) -> () { 310 | debug!("failure callback"); 311 | assert!(!context.is_null()); 312 | let selfclient : &mut ImmovableClient = unsafe {mem::transmute(context)}; 313 | if response.is_null() { 314 | selfclient.action_result = Some(Err(CallbackError::NullPtr)); 315 | } else { 316 | let resp : &mut ffiasync::MQTTAsync_failureData = unsafe {mem::transmute(response)}; 317 | selfclient.action_result = Some(Err(CallbackError::Response(resp.code))); 318 | } 319 | selfclient.barrier.wait(); 320 | } 321 | 322 | extern "C" fn received(context: *mut ::libc::c_void, topic_name: *mut ::libc::c_char, topic_len: ::libc::c_int, amessage: *mut ffiasync::MQTTAsync_message) -> i32 { 323 | let c_topic = unsafe {CStr::from_ptr(topic_name).to_bytes()}; 324 | let topic = String::from_utf8(c_topic.to_vec()).unwrap(); 325 | assert_eq!(topic.len(), topic_len as usize); 326 | 327 | assert!(!amessage.is_null()); 328 | let transmessage: &mut ffiasync::MQTTAsync_message = unsafe {mem::transmute(amessage)}; 329 | 330 | let payload = match transmessage.payloadlen { 331 | 0 => None, 332 | _ => { 333 | let payload_slice: &[u8] = unsafe { 334 | slice::from_raw_parts(transmessage.payload as *mut u8, transmessage.payloadlen as usize) 335 | }; 336 | Some(payload_slice.to_vec()) 337 | } 338 | }; 339 | 340 | assert!(!context.is_null()); 341 | let selfclient : &mut ImmovableClient = unsafe {mem::transmute(context)}; 342 | 343 | let qos = Qos::from_int(transmessage.qos); 344 | 345 | let retained: bool = match transmessage.retained { 346 | 0 => false, 347 | 1 => true, 348 | _ => unreachable!(), 349 | }; 350 | 351 | let duplicate: bool = match transmessage.dup { 352 | 0 => false, 353 | 1 => true, 354 | _ => unreachable!(), 355 | }; 356 | 357 | let msg = Message { 358 | topic : topic, 359 | payload : payload, 360 | qos : qos, 361 | retained : retained, 362 | duplicate : duplicate, 363 | }; 364 | 365 | // send message to channel or to iterator 366 | match selfclient.channel { 367 | Some(ref channel) => { 368 | channel.send(msg).unwrap(); 369 | } 370 | None => { 371 | let &(ref msglock, ref cvar) = &*selfclient.messages; 372 | let mut messages = msglock.lock().unwrap(); 373 | messages.push(msg); 374 | cvar.notify_one(); 375 | } 376 | } 377 | 378 | let mut msg = amessage; 379 | unsafe{ffiasync::MQTTAsync_freeMessage(&mut msg)}; 380 | unsafe{ffiasync::MQTTAsync_free(mem::transmute(topic_name))}; 381 | 1 382 | } 383 | 384 | } 385 | impl Drop for ImmovableClient { 386 | fn drop(&mut self) { 387 | unsafe{ffiasync::MQTTAsync_destroy(&mut self.handle)}; 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/async/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | use std::fmt; 26 | use std::error::Error; 27 | 28 | 29 | #[derive(Debug, Clone)] 30 | pub enum MqttError { 31 | Create(i32), 32 | Connect(ConnectError), 33 | Disconnect(DisconnectError), 34 | Subscribe(CommandError), 35 | Send(CommandError), 36 | } 37 | impl fmt::Display for MqttError { 38 | fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> { 39 | match *self { 40 | MqttError::Create(ref x) => fmt::Display::fmt(&format!("MqttError::Create({:?})", x), f), 41 | MqttError::Connect(ref x) => fmt::Display::fmt(&format!("MqttError::Connect({:?})", x), f), 42 | MqttError::Disconnect(ref x) => fmt::Display::fmt(&format!("MqttError::Disconnect({:?})", x), f), 43 | MqttError::Subscribe(ref x) => fmt::Display::fmt(&format!("MqttError::Subscribe({:?})", x), f), 44 | MqttError::Send(ref x) => fmt::Display::fmt(&format!("MqttError::Send({:?})", x), f), 45 | } 46 | } 47 | } 48 | impl Error for MqttError { 49 | fn description(&self) -> &str { 50 | match *self { 51 | MqttError::Create(_) => "Mqtt creation failed", 52 | MqttError::Connect(_) => "Mqtt connect failed", 53 | MqttError::Disconnect(_) => "Mqtt disconnect failed", 54 | MqttError::Subscribe(_) => "Mqtt subscribe failed", 55 | MqttError::Send(_) => "Mqtt send failed", 56 | } 57 | } 58 | } 59 | 60 | #[derive(Debug, Clone)] 61 | pub enum CommandError { 62 | ReturnCode(i32), 63 | CallbackResponse(i32), 64 | CallbackNullPtr 65 | } 66 | 67 | #[derive(Debug, Clone)] 68 | pub enum ConnectError { 69 | ReturnCode(ConnectErrReturnCode), 70 | CallbackResponse(i32), 71 | CallbackNullPtr 72 | } 73 | 74 | #[derive(Debug, Clone)] 75 | pub enum ConnectErrReturnCode { 76 | UnacceptableProtocol = 1, 77 | IdentifierRejected = 2, 78 | ServerUnavailable = 3, 79 | BadUsernameOrPassword = 4, 80 | NotAuthorized = 5, 81 | Reserved = 6, 82 | } 83 | impl ConnectErrReturnCode { 84 | pub fn from_int(i:i32) -> Self { 85 | match i { 86 | 1 => ConnectErrReturnCode::UnacceptableProtocol, 87 | 2 => ConnectErrReturnCode::IdentifierRejected, 88 | 3 => ConnectErrReturnCode::ServerUnavailable, 89 | 4 => ConnectErrReturnCode::BadUsernameOrPassword, 90 | 5 => ConnectErrReturnCode::NotAuthorized, 91 | 6 => ConnectErrReturnCode::Reserved, 92 | _ => unreachable!() 93 | } 94 | } 95 | } 96 | 97 | #[derive(Debug, Clone)] 98 | pub enum DisconnectError { 99 | ReturnCode(DisconnectErrReturnCode), 100 | CallbackResponse(i32), 101 | CallbackNullPtr 102 | } 103 | #[derive(Debug, Clone)] 104 | pub enum DisconnectErrReturnCode { 105 | GeneralFailure = -1, 106 | Disconnected = -3, 107 | } 108 | impl DisconnectErrReturnCode { 109 | pub fn from_int(i:i32) -> Self { 110 | match i { 111 | -1 => DisconnectErrReturnCode::GeneralFailure, 112 | -3 => DisconnectErrReturnCode::Disconnected, 113 | _ => unreachable!() 114 | } 115 | } 116 | } 117 | 118 | #[derive(Debug, Copy, Clone)] 119 | pub enum CallbackError { 120 | Response(i32), 121 | NullPtr 122 | } 123 | -------------------------------------------------------------------------------- /src/async/iterator.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | use time; 26 | use std::sync::{Arc, Mutex, Condvar}; 27 | use super::Message; 28 | 29 | 30 | pub struct AsyncClientIntoIterator { 31 | messages : Arc<(Mutex>, Condvar)>, 32 | timeout_ms : Option, 33 | } 34 | 35 | impl AsyncClientIntoIterator { 36 | pub fn new(messages: Arc<(Mutex>, Condvar)>, timeout_ms: Option) -> Self { 37 | AsyncClientIntoIterator{ messages : messages, 38 | timeout_ms : timeout_ms 39 | } 40 | } 41 | } 42 | 43 | impl Iterator for AsyncClientIntoIterator { 44 | type Item = Message; 45 | 46 | fn next(&mut self) -> Option { 47 | let &(ref msglock, ref cvar) = &*self.messages; 48 | 49 | if self.timeout_ms.is_some() { 50 | // non-blocking 51 | let deadline = time::now()+time::Duration::milliseconds(self.timeout_ms.unwrap() as i64); 52 | let mut messages = msglock.lock().unwrap(); 53 | let mut wait_duration; 54 | loop { 55 | if messages.len() > 0 { 56 | return Some(messages.remove(0)) 57 | } 58 | wait_duration = (deadline-time::now()).num_milliseconds(); 59 | if wait_duration <= 0 { 60 | // timeout before condvar wait 61 | return None 62 | } 63 | 64 | let (msgs, time_left) = cvar.wait_timeout_ms(messages, wait_duration as u32).unwrap(); 65 | if !time_left { 66 | // timeout - we did not get notification 67 | return None 68 | } 69 | else { 70 | messages = msgs; 71 | } 72 | } 73 | } 74 | else { 75 | // blocking 76 | let mut messages = msglock.lock().unwrap(); 77 | loop { 78 | if messages.len() > 0 { 79 | return Some(messages.remove(0)) 80 | } 81 | messages = cvar.wait(messages).unwrap(); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/async/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | mod client; 26 | mod error; 27 | mod iterator; 28 | mod options; 29 | 30 | pub use self::options::{PersistenceType, Qos, AsyncConnectOptions, AsyncDisconnectOptions}; 31 | pub use self::error::{MqttError, CommandError, ConnectError, ConnectErrReturnCode, DisconnectError, DisconnectErrReturnCode}; 32 | pub use self::iterator::AsyncClientIntoIterator; 33 | pub use self::client::AsyncClient; 34 | 35 | 36 | #[derive(Debug)] 37 | pub struct Message { 38 | pub topic : String, 39 | pub payload : Option>, 40 | pub qos : Qos, 41 | pub retained : bool, 42 | pub duplicate : bool, 43 | } 44 | -------------------------------------------------------------------------------- /src/async/options.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | use ffiasync; 26 | use std::ptr; 27 | 28 | #[derive(Debug, Copy, Clone)] 29 | pub enum PersistenceType { 30 | Default = 0, 31 | Nothing = 1, 32 | User = 2, 33 | } 34 | 35 | #[derive(Debug)] 36 | pub enum Qos { 37 | FireAndForget = 0, 38 | AtLeastOnce = 1, 39 | OnceAndOneOnly = 2, 40 | } 41 | impl Qos { 42 | pub fn from_int(i:i32) -> Self { 43 | match i { 44 | 0 => Qos::FireAndForget, 45 | 1 => Qos::AtLeastOnce, 46 | 2 => Qos::OnceAndOneOnly, 47 | _ => unreachable!(), 48 | } 49 | } 50 | } 51 | 52 | impl ffiasync::MQTTAsync_connectOptions { 53 | pub fn new() -> Self { 54 | ffiasync::MQTTAsync_connectOptions { 55 | struct_id : ['M' as i8, 'Q' as i8, 'T' as i8, 'C' as i8], 56 | struct_version : 3, 57 | keepAliveInterval : 60, 58 | cleansession : 1, 59 | maxInflight : 10, 60 | will : ptr::null_mut(), 61 | username : ptr::null_mut(), 62 | password : ptr::null_mut(), 63 | connectTimeout : 30, 64 | retryInterval : 0, 65 | ssl : ptr::null_mut(), 66 | onSuccess : None, 67 | onFailure : None, 68 | context : ptr::null_mut(), 69 | serverURIcount : 0, 70 | serverURIs : ptr::null_mut(), 71 | MQTTVersion : 0, 72 | } 73 | } 74 | } 75 | 76 | pub struct AsyncConnectOptions { 77 | pub keep_alive_interval : i32, 78 | pub cleansession : i32, 79 | pub max_in_flight : i32, 80 | pub connect_timeout : i32, 81 | pub retry_interval : i32, 82 | } 83 | impl AsyncConnectOptions { 84 | pub fn new() -> Self { 85 | AsyncConnectOptions { 86 | keep_alive_interval : 20, 87 | cleansession : 1, 88 | max_in_flight : 10, 89 | connect_timeout : 30, 90 | retry_interval : 0, 91 | } 92 | } 93 | } 94 | 95 | impl ffiasync::MQTTAsync_disconnectOptions { 96 | pub fn new() -> Self { 97 | ffiasync::MQTTAsync_disconnectOptions { 98 | struct_id : ['M' as i8, 'Q' as i8, 'T' as i8, 'D' as i8], 99 | struct_version : 0, 100 | timeout : 0, 101 | onSuccess : None, 102 | onFailure : None, 103 | context : ptr::null_mut(), 104 | } 105 | } 106 | } 107 | 108 | pub struct AsyncDisconnectOptions { 109 | pub timeout : i32, 110 | } 111 | impl AsyncDisconnectOptions { 112 | pub fn new() -> Self { 113 | AsyncDisconnectOptions { 114 | timeout : 30 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/ffiasync.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen */ 2 | 3 | pub type Persistence_open = 4 | ::std::option::Option ::libc::c_int>; 9 | pub type Persistence_close = 10 | ::std::option::Option ::libc::c_int>; 12 | pub type Persistence_put = 13 | ::std::option::Option ::libc::c_int>; 19 | pub type Persistence_get = 20 | ::std::option::Option ::libc::c_int>; 25 | pub type Persistence_remove = 26 | ::std::option::Option ::libc::c_int>; 29 | pub type Persistence_keys = 30 | ::std::option::Option ::libc::c_int>; 34 | pub type Persistence_clear = 35 | ::std::option::Option ::libc::c_int>; 37 | pub type Persistence_containskey = 38 | ::std::option::Option ::libc::c_int>; 41 | #[repr(C)] 42 | #[derive(Copy)] 43 | pub struct Struct_Unnamed1 { 44 | pub context: *mut ::libc::c_void, 45 | pub popen: Persistence_open, 46 | pub pclose: Persistence_close, 47 | pub pput: Persistence_put, 48 | pub pget: Persistence_get, 49 | pub premove: Persistence_remove, 50 | pub pkeys: Persistence_keys, 51 | pub pclear: Persistence_clear, 52 | pub pcontainskey: Persistence_containskey, 53 | } 54 | impl ::std::clone::Clone for Struct_Unnamed1 { 55 | fn clone(&self) -> Self { *self } 56 | } 57 | impl ::std::default::Default for Struct_Unnamed1 { 58 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 59 | } 60 | pub type MQTTClient_persistence = Struct_Unnamed1; 61 | pub type MQTTAsync = *mut ::libc::c_void; 62 | pub type MQTTAsync_token = ::libc::c_int; 63 | #[repr(C)] 64 | #[derive(Copy)] 65 | pub struct Struct_Unnamed2 { 66 | pub struct_id: [::libc::c_char; 4usize], 67 | pub struct_version: ::libc::c_int, 68 | pub payloadlen: ::libc::c_int, 69 | pub payload: *mut ::libc::c_void, 70 | pub qos: ::libc::c_int, 71 | pub retained: ::libc::c_int, 72 | pub dup: ::libc::c_int, 73 | pub msgid: ::libc::c_int, 74 | } 75 | impl ::std::clone::Clone for Struct_Unnamed2 { 76 | fn clone(&self) -> Self { *self } 77 | } 78 | impl ::std::default::Default for Struct_Unnamed2 { 79 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 80 | } 81 | pub type MQTTAsync_message = Struct_Unnamed2; 82 | pub type MQTTAsync_messageArrived = 83 | extern "C" fn(context: *mut ::libc::c_void, 84 | topicName: *mut ::libc::c_char, topicLen: ::libc::c_int, 85 | message: *mut MQTTAsync_message) -> ::libc::c_int; 86 | pub type MQTTAsync_deliveryComplete = 87 | extern "C" fn(context: *mut ::libc::c_void, token: MQTTAsync_token) -> (); 88 | pub type MQTTAsync_connectionLost = 89 | extern "C" fn(context: *mut ::libc::c_void, cause: *mut ::libc::c_char) 90 | -> (); 91 | #[repr(C)] 92 | #[derive(Copy)] 93 | pub struct Struct_Unnamed3 { 94 | pub token: MQTTAsync_token, 95 | pub code: ::libc::c_int, 96 | pub message: *mut ::libc::c_char, 97 | } 98 | impl ::std::clone::Clone for Struct_Unnamed3 { 99 | fn clone(&self) -> Self { *self } 100 | } 101 | impl ::std::default::Default for Struct_Unnamed3 { 102 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 103 | } 104 | pub type MQTTAsync_failureData = Struct_Unnamed3; 105 | #[repr(C)] 106 | #[derive(Copy)] 107 | pub struct Struct_Unnamed4 { 108 | pub token: MQTTAsync_token, 109 | pub alt: Union_Unnamed5, 110 | } 111 | impl ::std::clone::Clone for Struct_Unnamed4 { 112 | fn clone(&self) -> Self { *self } 113 | } 114 | impl ::std::default::Default for Struct_Unnamed4 { 115 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 116 | } 117 | #[repr(C)] 118 | #[derive(Copy)] 119 | pub struct Union_Unnamed5 { 120 | pub _bindgen_data_: [u64; 6usize], 121 | } 122 | impl Union_Unnamed5 { 123 | pub unsafe fn qos(&mut self) -> *mut ::libc::c_int { 124 | let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); 125 | ::std::mem::transmute(raw.offset(0)) 126 | } 127 | pub unsafe fn qosList(&mut self) -> *mut *mut ::libc::c_int { 128 | let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); 129 | ::std::mem::transmute(raw.offset(0)) 130 | } 131 | pub unsafe fn _pub(&mut self) -> *mut Struct_Unnamed6 { 132 | let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); 133 | ::std::mem::transmute(raw.offset(0)) 134 | } 135 | pub unsafe fn connect(&mut self) -> *mut Struct_Unnamed7 { 136 | let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_); 137 | ::std::mem::transmute(raw.offset(0)) 138 | } 139 | } 140 | impl ::std::clone::Clone for Union_Unnamed5 { 141 | fn clone(&self) -> Self { *self } 142 | } 143 | impl ::std::default::Default for Union_Unnamed5 { 144 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 145 | } 146 | #[repr(C)] 147 | #[derive(Copy)] 148 | pub struct Struct_Unnamed6 { 149 | pub message: MQTTAsync_message, 150 | pub destinationName: *mut ::libc::c_char, 151 | } 152 | impl ::std::clone::Clone for Struct_Unnamed6 { 153 | fn clone(&self) -> Self { *self } 154 | } 155 | impl ::std::default::Default for Struct_Unnamed6 { 156 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 157 | } 158 | #[repr(C)] 159 | #[derive(Copy)] 160 | pub struct Struct_Unnamed7 { 161 | pub serverURI: *mut ::libc::c_char, 162 | pub MQTTVersion: ::libc::c_int, 163 | pub sessionPresent: ::libc::c_int, 164 | } 165 | impl ::std::clone::Clone for Struct_Unnamed7 { 166 | fn clone(&self) -> Self { *self } 167 | } 168 | impl ::std::default::Default for Struct_Unnamed7 { 169 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 170 | } 171 | pub type MQTTAsync_successData = Struct_Unnamed4; 172 | pub type MQTTAsync_onSuccess = 173 | extern "C" fn(context: *mut ::libc::c_void, 174 | response: *mut MQTTAsync_successData) -> (); 175 | pub type MQTTAsync_onFailure = 176 | extern "C" fn(context: *mut ::libc::c_void, 177 | response: *mut MQTTAsync_failureData) -> (); 178 | #[repr(C)] 179 | #[derive(Copy)] 180 | pub struct Struct_Unnamed8 { 181 | pub struct_id: [::libc::c_char; 4usize], 182 | pub struct_version: ::libc::c_int, 183 | pub onSuccess: ::std::option::Option, 184 | pub onFailure: ::std::option::Option, 185 | pub context: *mut ::libc::c_void, 186 | pub token: MQTTAsync_token, 187 | } 188 | impl ::std::clone::Clone for Struct_Unnamed8 { 189 | fn clone(&self) -> Self { *self } 190 | } 191 | impl ::std::default::Default for Struct_Unnamed8 { 192 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 193 | } 194 | pub type MQTTAsync_responseOptions = Struct_Unnamed8; 195 | #[repr(C)] 196 | #[derive(Copy)] 197 | pub struct Struct_Unnamed9 { 198 | pub struct_id: [::libc::c_char; 4usize], 199 | pub struct_version: ::libc::c_int, 200 | pub topicName: *const ::libc::c_char, 201 | pub message: *const ::libc::c_char, 202 | pub retained: ::libc::c_int, 203 | pub qos: ::libc::c_int, 204 | } 205 | impl ::std::clone::Clone for Struct_Unnamed9 { 206 | fn clone(&self) -> Self { *self } 207 | } 208 | impl ::std::default::Default for Struct_Unnamed9 { 209 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 210 | } 211 | pub type MQTTAsync_willOptions = Struct_Unnamed9; 212 | #[repr(C)] 213 | #[derive(Copy)] 214 | pub struct Struct_Unnamed10 { 215 | pub struct_id: [::libc::c_char; 4usize], 216 | pub struct_version: ::libc::c_int, 217 | pub trustStore: *const ::libc::c_char, 218 | pub keyStore: *const ::libc::c_char, 219 | pub privateKey: *const ::libc::c_char, 220 | pub privateKeyPassword: *const ::libc::c_char, 221 | pub enabledCipherSuites: *const ::libc::c_char, 222 | pub enableServerCertAuth: ::libc::c_int, 223 | } 224 | impl ::std::clone::Clone for Struct_Unnamed10 { 225 | fn clone(&self) -> Self { *self } 226 | } 227 | impl ::std::default::Default for Struct_Unnamed10 { 228 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 229 | } 230 | pub type MQTTAsync_SSLOptions = Struct_Unnamed10; 231 | #[repr(C)] 232 | #[derive(Copy)] 233 | pub struct Struct_Unnamed11 { 234 | pub struct_id: [::libc::c_char; 4usize], 235 | pub struct_version: ::libc::c_int, 236 | pub keepAliveInterval: ::libc::c_int, 237 | pub cleansession: ::libc::c_int, 238 | pub maxInflight: ::libc::c_int, 239 | pub will: *mut MQTTAsync_willOptions, 240 | pub username: *const ::libc::c_char, 241 | pub password: *const ::libc::c_char, 242 | pub connectTimeout: ::libc::c_int, 243 | pub retryInterval: ::libc::c_int, 244 | pub ssl: *mut MQTTAsync_SSLOptions, 245 | pub onSuccess: ::std::option::Option, 246 | pub onFailure: ::std::option::Option, 247 | pub context: *mut ::libc::c_void, 248 | pub serverURIcount: ::libc::c_int, 249 | pub serverURIs: *const *mut ::libc::c_char, 250 | pub MQTTVersion: ::libc::c_int, 251 | } 252 | impl ::std::clone::Clone for Struct_Unnamed11 { 253 | fn clone(&self) -> Self { *self } 254 | } 255 | impl ::std::default::Default for Struct_Unnamed11 { 256 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 257 | } 258 | pub type MQTTAsync_connectOptions = Struct_Unnamed11; 259 | #[repr(C)] 260 | #[derive(Copy)] 261 | pub struct Struct_Unnamed12 { 262 | pub struct_id: [::libc::c_char; 4usize], 263 | pub struct_version: ::libc::c_int, 264 | pub timeout: ::libc::c_int, 265 | pub onSuccess: ::std::option::Option, 266 | pub onFailure: ::std::option::Option, 267 | pub context: *mut ::libc::c_void, 268 | } 269 | impl ::std::clone::Clone for Struct_Unnamed12 { 270 | fn clone(&self) -> Self { *self } 271 | } 272 | impl ::std::default::Default for Struct_Unnamed12 { 273 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 274 | } 275 | pub type MQTTAsync_disconnectOptions = Struct_Unnamed12; 276 | pub type Enum_MQTTASYNC_TRACE_LEVELS = ::libc::c_uint; 277 | pub const MQTTASYNC_TRACE_MAXIMUM: ::libc::c_uint = 1; 278 | pub const MQTTASYNC_TRACE_MEDIUM: ::libc::c_uint = 2; 279 | pub const MQTTASYNC_TRACE_MINIMUM: ::libc::c_uint = 3; 280 | pub const MQTTASYNC_TRACE_PROTOCOL: ::libc::c_uint = 4; 281 | pub const MQTTASYNC_TRACE_ERROR: ::libc::c_uint = 5; 282 | pub const MQTTASYNC_TRACE_SEVERE: ::libc::c_uint = 6; 283 | pub const MQTTASYNC_TRACE_FATAL: ::libc::c_uint = 7; 284 | pub type MQTTAsync_traceCallback = 285 | extern "C" fn(level: Enum_MQTTASYNC_TRACE_LEVELS, 286 | message: *mut ::libc::c_char) -> (); 287 | #[repr(C)] 288 | #[derive(Copy)] 289 | pub struct Struct_Unnamed13 { 290 | pub name: *const ::libc::c_char, 291 | pub value: *const ::libc::c_char, 292 | } 293 | impl ::std::clone::Clone for Struct_Unnamed13 { 294 | fn clone(&self) -> Self { *self } 295 | } 296 | impl ::std::default::Default for Struct_Unnamed13 { 297 | fn default() -> Self { unsafe { ::std::mem::zeroed() } } 298 | } 299 | pub type MQTTAsync_nameValue = Struct_Unnamed13; 300 | #[link(name = "paho-mqtt3a")] 301 | extern "C" { 302 | pub fn MQTTAsync_setCallbacks(handle: MQTTAsync, 303 | context: *mut ::libc::c_void, 304 | cl: ::std::option::Option, 305 | ma: ::std::option::Option, 306 | dc: ::std::option::Option) 307 | -> ::libc::c_int; 308 | pub fn MQTTAsync_create(handle: *mut MQTTAsync, 309 | serverURI: *const ::libc::c_char, 310 | clientId: *const ::libc::c_char, 311 | persistence_type: ::libc::c_int, 312 | persistence_context: *mut ::libc::c_void) 313 | -> ::libc::c_int; 314 | pub fn MQTTAsync_connect(handle: MQTTAsync, 315 | options: *const MQTTAsync_connectOptions) 316 | -> ::libc::c_int; 317 | pub fn MQTTAsync_disconnect(handle: MQTTAsync, 318 | options: *const MQTTAsync_disconnectOptions) 319 | -> ::libc::c_int; 320 | pub fn MQTTAsync_isConnected(handle: MQTTAsync) -> ::libc::c_int; 321 | pub fn MQTTAsync_subscribe(handle: MQTTAsync, 322 | topic: *const ::libc::c_char, 323 | qos: ::libc::c_int, 324 | response: *mut MQTTAsync_responseOptions) 325 | -> ::libc::c_int; 326 | pub fn MQTTAsync_subscribeMany(handle: MQTTAsync, count: ::libc::c_int, 327 | topic: *const *mut ::libc::c_char, 328 | qos: *mut ::libc::c_int, 329 | response: *mut MQTTAsync_responseOptions) 330 | -> ::libc::c_int; 331 | pub fn MQTTAsync_unsubscribe(handle: MQTTAsync, 332 | topic: *const ::libc::c_char, 333 | response: *mut MQTTAsync_responseOptions) 334 | -> ::libc::c_int; 335 | pub fn MQTTAsync_unsubscribeMany(handle: MQTTAsync, count: ::libc::c_int, 336 | topic: *const *mut ::libc::c_char, 337 | response: *mut MQTTAsync_responseOptions) 338 | -> ::libc::c_int; 339 | pub fn MQTTAsync_send(handle: MQTTAsync, 340 | destinationName: *const ::libc::c_char, 341 | payloadlen: ::libc::c_int, 342 | payload: *mut ::libc::c_void, qos: ::libc::c_int, 343 | retained: ::libc::c_int, 344 | response: *mut MQTTAsync_responseOptions) 345 | -> ::libc::c_int; 346 | pub fn MQTTAsync_sendMessage(handle: MQTTAsync, 347 | destinationName: *const ::libc::c_char, 348 | msg: *const MQTTAsync_message, 349 | response: *mut MQTTAsync_responseOptions) 350 | -> ::libc::c_int; 351 | pub fn MQTTAsync_getPendingTokens(handle: MQTTAsync, 352 | tokens: *mut *mut MQTTAsync_token) 353 | -> ::libc::c_int; 354 | pub fn MQTTAsync_isComplete(handle: MQTTAsync, dt: MQTTAsync_token) 355 | -> ::libc::c_int; 356 | pub fn MQTTAsync_waitForCompletion(handle: MQTTAsync, dt: MQTTAsync_token, 357 | timeout: ::libc::c_ulong) 358 | -> ::libc::c_int; 359 | pub fn MQTTAsync_freeMessage(msg: *mut *mut MQTTAsync_message) -> (); 360 | pub fn MQTTAsync_free(ptr: *mut ::libc::c_void) -> (); 361 | pub fn MQTTAsync_destroy(handle: *mut MQTTAsync) -> (); 362 | pub fn MQTTAsync_setTraceLevel(level: Enum_MQTTASYNC_TRACE_LEVELS) -> (); 363 | pub fn MQTTAsync_setTraceCallback(callback: 364 | *mut ::std::option::Option 366 | ()>) 367 | -> (); 368 | pub fn MQTTAsync_getVersionInfo() -> *mut MQTTAsync_nameValue; 369 | } 370 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #[macro_use] 26 | extern crate log; 27 | extern crate time; 28 | extern crate libc; 29 | 30 | #[allow(non_camel_case_types)] 31 | #[allow(non_snake_case)] 32 | #[allow(dead_code)] 33 | mod ffiasync; 34 | pub mod async; 35 | --------------------------------------------------------------------------------