From fbfeaa6e49027d1cb88e04bb9822f0b11ffa46c6 Mon Sep 17 00:00:00 2001 From: Mark Radbourne Date: Tue, 3 Nov 2020 10:33:29 -0800 Subject: [PATCH] Initial code --- .gitignore | 3 + Cargo.toml | 16 ++++ README.md | 14 ++- src/az_core.rs | 56 +++++++++++ src/az_iot.rs | 251 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 11 +++ 6 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 Cargo.toml create mode 100644 src/az_core.rs create mode 100644 src/az_iot.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore index 088ba6b..720bbed 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# Ignore VS Code files +/.vscode/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ebce628 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "azure-embedded-sdk-rs" +version = "0.1.0" +authors = ["Mark Radbourne "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# azure-embedded-sdk-sys = { path = "../azure-embedded-sdk-sys" } +azure-embedded-sdk-sys = { git = "https://github.com/markrad/azure-embedded-sdk-sys.git", tag = "v0.1.0" } + +[dev-dependencies] +regex = "1.4.1" +base64 = "0.12.3" +hmac-sha256 = "0.1.6" diff --git a/README.md b/README.md index 12fe42e..6b5095a 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# azure-embedded-sdk-rs \ No newline at end of file +# azure-embedded-sdk-rs +This builds upon the crate in https://github.com/markrad/azure-embedded-sdk-sys which is a thin bindgen later over the Azure Embedded C SDK at https://github.com/azure/azure-sdk-for-c. + +It only addresses the IoT portion of the SDK (and may only ever). Examples are not included since, to make a useful example, it would need to select a MQTT library and TLS library in order for that example to demonstrate how to connect to an IoT hub. + +Instead, an example is provided in https://github.com/markrad/azure-embedded-sdk-rs-example (which, at time of writing does not actually exist). + +To use simply add +```ini +[dependancies] +azure-embedded-sdk-rs = { git = "https://github.com/markrad/azure-embedded-sdk-rs.git", tag = "" } +``` +I recommend using a tag since this is a work in progress but by omitting the tag keyword and value the latest version will be cloned. diff --git a/src/az_core.rs b/src/az_core.rs new file mode 100644 index 0000000..b7d975c --- /dev/null +++ b/src/az_core.rs @@ -0,0 +1,56 @@ +use azsys; + +pub fn get_span_from_str(s: &str) -> azsys::az_span { + let result: azsys::az_span = azsys::az_span { + _internal: azsys::az_span__bindgen_ty_1 { + ptr: s.as_ptr() as *mut u8, + size: s.len() as i32, + } + }; + + result +} + +pub fn get_span_from_vector(v: &Vec) -> azsys::az_span { + let result: azsys::az_span = azsys::az_span { + _internal: azsys::az_span__bindgen_ty_1 { + ptr: v.as_ptr() as *mut u8, + size: v.capacity() as i32, + } + }; + + result +} + +pub fn get_empty_span() -> azsys::az_span { + let result: azsys::az_span = azsys::az_span { + _internal: azsys::az_span__bindgen_ty_1 { + ptr: std::ptr::null_mut(), + size: 0, + } + }; + + result +} + +pub fn get_span_size(span: &azsys::az_span) -> i32 { + span._internal.size +} +/* +pub struct az_span { + inner: azsys::az_span; +} + +impl az_span { + pub fn from_str(s: &str) -> az_span { + let result: azsys::az_span = azsys::az_span { + _internal: azsys::az_span__bindgen_ty_1 { + ptr: s.as_ptr() as *mut u8, + size: s.len() as i32, + } + }; + + result + } +} +*/ \ No newline at end of file diff --git a/src/az_iot.rs b/src/az_iot.rs new file mode 100644 index 0000000..f7c6019 --- /dev/null +++ b/src/az_iot.rs @@ -0,0 +1,251 @@ +use azsys; +pub use crate::az_core::*; +use std::str; +use std::slice; + +//pub mod az_core; + +pub struct HubClient { + inner: azsys::az_iot_hub_client, +} + +impl HubClient { + + pub fn new(host_name: &str, device_id: &str, options: Option) -> Result { + + let options_work: *const azsys::az_iot_hub_client_options; + + match options { + Some(o) => options_work = &o.inner, + None => options_work = std::ptr::null(), + } + + let mut client: HubClient = HubClient::empty_client(); + let rc = unsafe { azsys::az_iot_hub_client_init(&mut client.inner, + get_span_from_str(host_name), + get_span_from_str(device_id), + options_work + )}; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + Ok(client) + } + } + + pub fn get_client_id(&self) -> Result { + + let mut result_work: Vec = Vec::with_capacity(100); + let mut result_length: Vec = [ 0 ].to_vec(); + + let rc = unsafe { azsys::az_iot_hub_client_get_client_id(&self.inner, + result_work.as_mut_ptr() as *mut i8, + result_work.capacity()as u64, + result_length.as_mut_ptr()) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + unsafe { result_work.set_len(result_length[0] as usize) }; + Ok(String::from_utf8_lossy(&result_work).to_string()) + } + } + + pub fn get_user_name(&self) -> Result { + + let mut result_work: Vec = Vec::with_capacity(200); + let mut result_length: Vec = [ 0 ].to_vec(); + + let rc = unsafe { azsys::az_iot_hub_client_get_user_name(&self.inner, + result_work.as_mut_ptr() as *mut i8, + result_work.capacity()as u64, + result_length.as_mut_ptr()) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + unsafe { result_work.set_len(result_length[0] as usize) }; + Ok(String::from_utf8_lossy(&result_work).to_string()) + } + } + + pub fn get_client_telemetry_publish_topic(&self, message_properties: Option) -> Result { + + let m_prop_work: *const azsys::az_iot_message_properties; + + match message_properties { + Some(m) => m_prop_work = &m.inner, + None => m_prop_work = std::ptr::null() + } + + let mut result_work: Vec = Vec::with_capacity(200); + let mut result_length: Vec = [ 0 ].to_vec(); + let rc = unsafe { azsys::az_iot_hub_client_telemetry_get_publish_topic(&self.inner, + m_prop_work, + result_work.as_mut_ptr() as *mut i8, + result_work.capacity() as u64, + result_length.as_mut_ptr() + ) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + unsafe { result_work.set_len(result_length[0] as usize) }; + //let test = result_work.to_str(); + Ok(String::from_utf8_lossy(&result_work).to_string()) + } + } + + pub fn get_sas_signature(&self, ttl: u64) -> Result, azsys::az_result> { + let mut signature_work: Vec = Vec::with_capacity(200); + let signature = get_span_from_vector(&signature_work); + let mut work = get_empty_span(); + + let rc = unsafe { azsys::az_iot_hub_client_sas_get_signature(&self.inner, ttl, signature, &mut work) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + unsafe { signature_work.set_len(get_span_size(&work) as usize) }; + Ok(signature_work) + } + } + + pub fn get_sas_password(&self, ttl: u64, sas: &str) -> Result { + let mut password: Vec = Vec::with_capacity(200); + let mut length_out: Vec = [ 0 ].to_vec(); + let rc = unsafe { azsys::az_iot_hub_client_sas_get_password(&self.inner, + ttl, + get_span_from_str(sas), + get_empty_span(), + password.as_mut_ptr() as *mut i8, + password.capacity() as u64, + length_out.as_mut_ptr() + ) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + unsafe { password.set_len(length_out[0] as usize) }; + Ok(String::from_utf8_lossy(&password).to_string()) + } + } + + pub fn empty_client() -> HubClient { + let client: HubClient = HubClient { + inner: azsys::az_iot_hub_client { + _internal: azsys::az_iot_hub_client__bindgen_ty_1 { + iot_hub_hostname: get_empty_span(), + device_id: get_empty_span(), + options: HubClientOptions::default_new().inner, + } + } + }; + + client + } +} + +pub struct HubClientOptions { + inner: azsys::az_iot_hub_client_options, +} + +impl HubClientOptions { + + pub fn default_new() -> HubClientOptions { + HubClientOptions { + inner: unsafe { azsys::az_iot_hub_client_options_default() } + } + } +} + +pub struct MessageProperties { + inner: azsys::az_iot_message_properties, +} + +impl MessageProperties { + pub fn new(buffer: Vec) -> Result { + let mut message_properties: MessageProperties = MessageProperties { + inner: azsys::az_iot_message_properties { + _internal: azsys::az_iot_message_properties__bindgen_ty_1 { + properties_buffer: get_empty_span(), + properties_written: 0, + current_property_index: 0, + } + } + }; + + let rc = unsafe { azsys::az_iot_message_properties_init(&mut message_properties.inner, get_span_from_vector(&buffer), 0) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + Ok(message_properties) + } + } + + pub fn append(&mut self, k: &str, v: &str) -> Result<&mut MessageProperties, azsys::az_result_core> { + let rc = unsafe { azsys::az_iot_message_properties_append(&mut self.inner, get_span_from_str(k), get_span_from_str(v)) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + Ok(self) + } + } + + pub fn find(&mut self, k: &str) -> Result<&str, azsys::az_result_core> { + let mut out = get_empty_span(); + let rc = unsafe { azsys::az_iot_message_properties_find(&mut self.inner, get_span_from_str(k), &mut out) }; + + if rc != azsys::az_result_core_AZ_OK { + Err(rc) + } + else { + let slice = unsafe { slice::from_raw_parts(out._internal.ptr, out._internal.size as usize) }; + // let r = unsafe { + // let slice = slice::from_raw_parts(out._internal.ptr, out._internal.size as usize); + // str::from_utf8(slice) + // }; + Ok(str::from_utf8(slice).expect("Value is not in UTF8")) + } + } + + pub fn into_array(&mut self) -> Result, std::os::raw::c_int> { + + let mut out: Vec<(&str, &str)> = Vec::new(); + let mut k = get_empty_span(); + let mut v = get_empty_span(); + + loop { + let rc = unsafe { azsys::az_iot_message_properties_next(&mut self.inner, &mut k, &mut v) as ::std::os::raw::c_int }; + + if rc == azsys::az_result_core_AZ_OK as ::std::os::raw::c_int { + let slicek = unsafe { slice::from_raw_parts(k._internal.ptr, k._internal.size as usize) }; + let slicev = unsafe { slice::from_raw_parts(v._internal.ptr, v._internal.size as usize) }; + let ks = str::from_utf8(slicek).expect("keyword is not in UTF8"); + let vs = str::from_utf8(slicev).expect("Value is not in UTF8"); + out.push((ks, vs)); + // out.push((str::from_utf8(slicek).expect("keyword is not in UTF8"), + // str::from_utf8(slicev).expect("Value is not in UTF8"))).expect("Failed to store keyword/value"); + } + else if rc == azsys::az_result_iot_AZ_ERROR_IOT_END_OF_PROPERTIES as std::os::raw::c_int { + break; + } + else { + return Err(rc); + } + } + + Ok(out) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0195ce5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +extern crate azure_embedded_sdk_sys as azsys; + +pub use az_iot::*; +pub use az_core::*; + +pub mod az_iot; +pub mod az_core; + +#[cfg(test)] +mod tests { +}