diff --git a/Cargo.toml b/Cargo.toml index 30fa5a901..fc9e01bd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ members = [ "azure_sdk_storage_blob", "azure_sdk_storage_core", "azure_sdk_storage_table", - "azure_sdk_cosmos" + "azure_sdk_cosmos", + "azure_sdk_storage_queue" ] diff --git a/azure_sdk_core/Cargo.toml b/azure_sdk_core/Cargo.toml index 871e4dea0..fe98fd794 100644 --- a/azure_sdk_core/Cargo.toml +++ b/azure_sdk_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "azure_sdk_core" -version = "0.43.6" +version = "0.43.7" description = "Rust wrappers around Microsoft Azure REST APIs - Core crate" readme = "README.md" authors = ["Francesco Cogno ", "Max Gortman ", "Dong Liu "] diff --git a/azure_sdk_core/src/errors.rs b/azure_sdk_core/src/errors.rs index 574b2fa91..4b6ac3030 100644 --- a/azure_sdk_core/src/errors.rs +++ b/azure_sdk_core/src/errors.rs @@ -1,12 +1,6 @@ use crate::{enumerations::ParsingError, range::ParseError}; -use base64; -use chrono; -use http; use http::header::ToStrError; use hyper::{self, body, Body, StatusCode}; -use serde_json; -use serde_xml_rs; -use std; use std::io::Error as IOError; use std::num; use std::num::ParseIntError; @@ -14,7 +8,6 @@ use std::str; use std::str::ParseBoolError; use std::string; use url::ParseError as URLParseError; -use uuid; use xml::BuilderError as XMLError; quick_error! { diff --git a/azure_sdk_core/src/lib.rs b/azure_sdk_core/src/lib.rs index ce00bd085..d690a880a 100644 --- a/azure_sdk_core/src/lib.rs +++ b/azure_sdk_core/src/lib.rs @@ -36,6 +36,7 @@ use crate::lease::LeaseId; use http::request::Builder; use http::HeaderMap; use std::collections::HashMap; +use std::convert::TryFrom; mod stored_access_policy; pub use self::stored_access_policy::{StoredAccessPolicy, StoredAccessPolicyList}; pub mod prelude; @@ -486,6 +487,14 @@ pub trait IncludeMetadataSupport { pub trait IncludeMetadataOption { fn include_metadata(&self) -> bool; + + fn to_uri_parameter(&self) -> Option<&'static str> { + if self.include_metadata() { + Some("include=metadata") + } else { + None + } + } } pub trait IncludeCopySupport { @@ -524,7 +533,7 @@ pub trait IncludeListOptions: if self.include_metadata() { if !f_first { - s.push_str(","); + s.push(','); } s.push_str("metadata"); f_first = false; @@ -532,7 +541,7 @@ pub trait IncludeListOptions: if self.include_uncommitted_blobs() { if !f_first { - s.push_str(","); + s.push(','); } s.push_str("uncommittedblobs"); f_first = false; @@ -540,7 +549,7 @@ pub trait IncludeListOptions: if self.include_copy() { if !f_first { - s.push_str(","); + s.push(','); } s.push_str("copy"); f_first = false; @@ -548,7 +557,7 @@ pub trait IncludeListOptions: if self.include_deleted() { if !f_first { - s.push_str(","); + s.push(','); } s.push_str("deleted"); } @@ -935,6 +944,10 @@ pub fn request_id_from_headers(headers: &HeaderMap) -> Result Option { + headers.get_as_str(CLIENT_REQUEST_ID).map(|s| s.to_owned()) +} + pub fn content_md5_from_headers_optional( headers: &HeaderMap, ) -> Result, AzureError> { @@ -945,6 +958,29 @@ pub fn content_md5_from_headers_optional( } } +#[derive(Debug, Clone)] +pub struct CommonStorageResponseHeaders { + pub request_id: RequestId, + pub client_request_id: Option, + pub version: String, + pub date: DateTime, + pub server: String, +} + +impl TryFrom<&HeaderMap> for CommonStorageResponseHeaders { + type Error = AzureError; + + fn try_from(headers: &HeaderMap) -> Result { + Ok(Self { + request_id: request_id_from_headers(headers)?, + client_request_id: client_request_id_from_headers_optional(headers), + version: version_from_headers(headers)?.to_owned(), + date: date_from_headers(headers)?, + server: server_from_headers(headers)?.to_owned(), + }) + } +} + pub fn content_md5_from_headers(headers: &HeaderMap) -> Result<[u8; 16], AzureError> { let content_md5 = headers .get(CONTENT_MD5) @@ -998,10 +1034,8 @@ pub fn content_crc64_from_headers(headers: &HeaderMap) -> Result<[u8; 8], AzureE pub fn consistency_from_headers(headers: &HeaderMap) -> Result { if let Some(content_crc64) = content_crc64_from_headers_optional(headers)? { return Ok(Consistency::Crc64(content_crc64)); - } else { - if let Some(content_md5) = content_md5_from_headers_optional(headers)? { - return Ok(Consistency::Md5(content_md5)); - } + } else if let Some(content_md5) = content_md5_from_headers_optional(headers)? { + return Ok(Consistency::Md5(content_md5)); } Err(AzureError::HeadersNotFound(vec![ @@ -1045,6 +1079,12 @@ pub fn continuation_token_from_headers_optional( } } +#[inline] +pub fn utc_date_from_rfc2822(date: &str) -> Result, AzureError> { + let date = DateTime::parse_from_rfc2822(date)?; + Ok(DateTime::from_utc(date.naive_utc(), Utc)) +} + pub fn date_from_headers(headers: &HeaderMap) -> Result, AzureError> { let date = headers .get(DATE) diff --git a/azure_sdk_core/src/parsing.rs b/azure_sdk_core/src/parsing.rs index 23002d6be..dea9b43cb 100644 --- a/azure_sdk_core/src/parsing.rs +++ b/azure_sdk_core/src/parsing.rs @@ -1,5 +1,4 @@ use crate::errors::TraversingError; -use chrono; use xml::Element; use xml::Xml::{CharacterNode, ElementNode}; diff --git a/azure_sdk_storage_core/Cargo.toml b/azure_sdk_storage_core/Cargo.toml index f2399aa97..753b103f6 100644 --- a/azure_sdk_storage_core/Cargo.toml +++ b/azure_sdk_storage_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "azure_sdk_storage_core" -version = "0.44.3" +version = "0.44.4" description = "Rust wrappers around Microsoft Azure REST APIs - Core storage crate" readme = "README.md" authors = ["Francesco Cogno ", "Max Gortman ", "Dong Liu "] @@ -15,7 +15,7 @@ categories = ["api-bindings"] edition = "2018" [dependencies] -azure_sdk_core = { path = "../azure_sdk_core", version = "0.43.5" } +azure_sdk_core = { path = "../azure_sdk_core", version = "0.43.6" } ring = "0.16" base64 = "0.12" chrono = "0.4" diff --git a/azure_sdk_storage_core/src/bearer_token_client.rs b/azure_sdk_storage_core/src/bearer_token_client.rs index 0d8e41b12..ad2e6b93f 100644 --- a/azure_sdk_storage_core/src/bearer_token_client.rs +++ b/azure_sdk_storage_core/src/bearer_token_client.rs @@ -14,6 +14,7 @@ pub struct BearerTokenClient<'a> { hc: hyper::Client>, blob_uri: String, table_uri: String, + queue_uri: String, } impl<'a> BearerTokenClient<'a> { @@ -25,10 +26,12 @@ impl<'a> BearerTokenClient<'a> { ) -> Self { let blob_uri = format!("https://{}.blob.core.windows.net", account); let table_uri = format!("https://{}.table.core.windows.net", account); + let queue_uri = format!("https://{}.queue.core.windows.net", account); Self { account, bearer_token, + queue_uri, hc, blob_uri, table_uri, @@ -90,6 +93,11 @@ impl<'a> Client for BearerTokenClient<'a> { &self.table_uri } + #[inline] + fn queue_uri(&self) -> &str { + &self.queue_uri + } + #[inline] fn perform_request( &self, diff --git a/azure_sdk_storage_core/src/client.rs b/azure_sdk_storage_core/src/client.rs index 16a5355a0..d6bb2b741 100644 --- a/azure_sdk_storage_core/src/client.rs +++ b/azure_sdk_storage_core/src/client.rs @@ -13,9 +13,10 @@ pub trait HttpHeaderAdder { fn add_headers(&self, builder: ::http::request::Builder) -> ::http::request::Builder; } -pub trait Client: Send + Sync { +pub trait Client: std::fmt::Debug + Send + Sync { fn blob_uri(&self) -> &str; fn table_uri(&self) -> &str; + fn queue_uri(&self) -> &str; /// Uri scheme + authority e.g. http://myaccount.table.core.windows.net/ #[inline] @@ -53,6 +54,9 @@ where fn table_uri(&self) -> &str { self.as_ref().table_uri() } + fn queue_uri(&self) -> &str { + self.as_ref().queue_uri() + } fn perform_request( &self, @@ -87,6 +91,9 @@ where fn table_uri(&self) -> &str { self.as_ref().table_uri() } + fn queue_uri(&self) -> &str { + self.as_ref().queue_uri() + } fn perform_request( &self, @@ -133,6 +140,7 @@ pub fn with_azure_sas(account: &str, sas_token: &str) -> KeyClient { client, format!("https://{}.blob.core.windows.net", account), format!("https://{}.table.core.windows.net", account), + format!("https://{}.queue.core.windows.net", account), ) } @@ -146,6 +154,7 @@ pub fn with_access_key(account: &str, key: &str) -> KeyClient { client, format!("https://{}.blob.core.windows.net", account), format!("https://{}.table.core.windows.net", account), + format!("https://{}.queue.core.windows.net", account), ) } @@ -167,6 +176,7 @@ pub fn from_connection_string(connection_string: &str) -> Result Result Result { Err(AzureError::GenericErrorWithText( @@ -219,6 +231,8 @@ pub fn with_emulator(blob_storage_url: &Url, table_storage_url: &Url) -> KeyClie debug!("blob_uri == {}", blob_uri); let table_uri = format!("{}devstoreaccount1", table_storage_url.as_str()); debug!("table_uri == {}", table_uri); + let queue_uri = format!("{}devstoreaccount1", table_storage_url.as_str()); + debug!("queue_uri == {}", queue_uri); KeyClient::new( "devstoreaccount1".to_owned(), @@ -228,5 +242,6 @@ pub fn with_emulator(blob_storage_url: &Url, table_storage_url: &Url) -> KeyClie client, blob_uri, table_uri, + queue_uri, ) } diff --git a/azure_sdk_storage_core/src/key_client.rs b/azure_sdk_storage_core/src/key_client.rs index 9f4570dd0..b6def34ff 100644 --- a/azure_sdk_storage_core/src/key_client.rs +++ b/azure_sdk_storage_core/src/key_client.rs @@ -14,6 +14,7 @@ pub struct KeyClient { hc: hyper::Client>, blob_uri: String, table_uri: String, + queue_uri: String, } pub(crate) fn get_sas_token_parms(sas_token: &str) -> Vec<(String, String)> { @@ -37,6 +38,7 @@ impl KeyClient { hc: hyper::Client>, blob_uri: String, table_uri: String, + queue_uri: String, ) -> Self { Self { account, @@ -45,6 +47,7 @@ impl KeyClient { hc, blob_uri, table_uri, + queue_uri, } } @@ -67,6 +70,11 @@ impl Client for KeyClient { &self.table_uri } + #[inline] + fn queue_uri(&self) -> &str { + &self.queue_uri + } + fn perform_request( &self, uri: &str, diff --git a/azure_sdk_storage_core/src/rest_client.rs b/azure_sdk_storage_core/src/rest_client.rs index 6493c6f7b..8f40f1569 100644 --- a/azure_sdk_storage_core/src/rest_client.rs +++ b/azure_sdk_storage_core/src/rest_client.rs @@ -406,7 +406,7 @@ fn canonicalize_header(h: &HeaderMap) -> String { .filter(|(k, _v)| k.as_str().starts_with("x-ms")) .map(|(k, _)| k.as_str()) .collect::>(); - v_headers.sort(); + v_headers.sort_unstable(); let mut can = String::new(); @@ -434,7 +434,7 @@ fn canonicalized_resource(client_endpoint: &CE, u: &url::Url { let mut path = String::new(); for p in paths { - path.push_str("/"); + path.push('/'); path.push_str(&*p); } diff --git a/azure_sdk_storage_queue/Cargo.toml b/azure_sdk_storage_queue/Cargo.toml new file mode 100644 index 000000000..8c62b604e --- /dev/null +++ b/azure_sdk_storage_queue/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "azure_sdk_storage_queue" +version = "0.1.0" +description = "Rust wrappers around Microsoft Azure REST APIs - Azure Storage Queue crate" +readme = "README.md" +authors = ["Francesco Cogno "] +license = "Apache-2.0" +repository = "https://github.com/MindFlavor/AzureSDKForRust" +documentation = "http://mindflavor.github.io/AzureSDKForRust/azure_sdk_for_rust/index.html" +homepage = "https://github.com/MindFlavor/AzureSDKForRust" + +keywords = ["sdk", "azure", "rest", "iot", "cloud"] +categories = ["api-bindings"] + +edition = "2018" + +[dependencies] +azure_sdk_core = { path = "../azure_sdk_core", version = "0.43.7" } +azure_sdk_storage_core = { path = "../azure_sdk_storage_core", version = "0.44.4" } +ring = "0.16" +base64 = "0.12" +chrono = "0.4" +http = "0.2" +futures = "0.3" +hyper = "0.13" +log = "0.4" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +serde-xml-rs = "0.4" +url = "2.1" +uuid = { version = "0.8", features = ["v4"] } +hyper-rustls = "0.20" +failure = "0.1" + +[dev-dependencies] +env_logger = "0.7" +tokio = { version = "0.2", features = ["macros"] } +serde = { version = "1.0", features = ["derive"] } + +[features] +test_e2e = [] diff --git a/azure_sdk_storage_queue/examples/list_queues.rs b/azure_sdk_storage_queue/examples/list_queues.rs new file mode 100644 index 000000000..f4091341f --- /dev/null +++ b/azure_sdk_storage_queue/examples/list_queues.rs @@ -0,0 +1,32 @@ +#[macro_use] +extern crate log; + +use azure_sdk_core::prelude::*; +use azure_sdk_storage_core::prelude::*; +use azure_sdk_storage_queue::prelude::*; +use std::error::Error; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // First we retrieve the account name and master key from environment variables. + let account = + std::env::var("STORAGE_ACCOUNT").expect("Set env variable STORAGE_ACCOUNT first!"); + let master_key = + std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!"); + + let client = client::with_access_key(&account, &master_key).into_queue_service_client(); + + trace!("enumerating queues"); + + let response = client + .list_queues() + .with_prefix("a") + .with_include_metadata() + .with_max_results(2) + .execute() + .await?; + + println!("response == {:#?}", response); + + Ok(()) +} diff --git a/azure_sdk_storage_queue/examples/put_message.rs b/azure_sdk_storage_queue/examples/put_message.rs new file mode 100644 index 000000000..5a91174c4 --- /dev/null +++ b/azure_sdk_storage_queue/examples/put_message.rs @@ -0,0 +1,35 @@ +#[macro_use] +extern crate log; +use azure_sdk_core::prelude::*; +use azure_sdk_storage_core::prelude::*; +use azure_sdk_storage_queue::prelude::*; +use std::error::Error; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // First we retrieve the account name and master key from environment variables. + let account = + std::env::var("STORAGE_ACCOUNT").expect("Set env variable STORAGE_ACCOUNT first!"); + let master_key = + std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!"); + + let queue_name = std::env::args() + .nth(1) + .expect("Please pass the queue name as first parameter"); + + let client = client::with_access_key(&account, &master_key).into_queue_service_client(); + + trace!("enumerating queues"); + + let response = client + .with_queue_name_client(&queue_name) + .put_message() + .with_client_request_id("optional correlation token") + .with_message_body("Azure SDK for Rust rocks!") + .execute() + .await?; + + println!("response == {:#?}", response); + + Ok(()) +} diff --git a/azure_sdk_storage_queue/src/clients/mod.rs b/azure_sdk_storage_queue/src/clients/mod.rs new file mode 100644 index 000000000..d26f3bba1 --- /dev/null +++ b/azure_sdk_storage_queue/src/clients/mod.rs @@ -0,0 +1,4 @@ +mod queue_name_client; +mod queue_service_client; +pub use queue_name_client::QueueNameClient; +pub use queue_service_client::QueueServiceClient; diff --git a/azure_sdk_storage_queue/src/clients/queue_name_client.rs b/azure_sdk_storage_queue/src/clients/queue_name_client.rs new file mode 100644 index 000000000..9075f8797 --- /dev/null +++ b/azure_sdk_storage_queue/src/clients/queue_name_client.rs @@ -0,0 +1,74 @@ +use crate::clients::QueueServiceClient; +use crate::requests; +use crate::{HasStorageClient, IntoQueueNameClient, QueueNameService, WithQueueNameClient}; +use azure_sdk_core::No; +use azure_sdk_storage_core::Client; +use std::borrow::Cow; +use std::fmt::Debug; + +#[derive(Debug, Clone)] +pub struct QueueNameClient<'a, 'b, C> +where + C: Client + Clone, +{ + pub storage_client: Cow<'a, C>, + pub queue_name: Cow<'b, str>, +} + +impl<'a, 'b, C> HasStorageClient for QueueNameClient<'a, 'b, C> +where + C: Client + Clone, +{ + type StorageClient = C; + + fn storage_client(&self) -> &C { + self.storage_client.as_ref() + } +} + +impl<'a, 'b, C> WithQueueNameClient<'a, 'b> for QueueServiceClient<'a, C> +where + C: Client + Clone, +{ + type QueueNameClient = QueueNameClient<'a, 'b, C>; + + fn with_queue_name_client(&'a self, queue_name: NAME) -> Self::QueueNameClient + where + NAME: Into>, + { + QueueNameClient { + storage_client: Cow::Borrowed(&self.storage_client), + queue_name: queue_name.into(), + } + } +} + +impl<'a, 'b, C> IntoQueueNameClient<'b> for QueueServiceClient<'a, C> +where + C: Client + Clone, +{ + type QueueNameClient = QueueNameClient<'a, 'b, C>; + + fn into_queue_name_client(self, queue_name: NAME) -> Self::QueueNameClient + where + NAME: Into>, + { + QueueNameClient { + storage_client: Cow::Owned(self.storage_client.into_owned()), + queue_name: queue_name.into(), + } + } +} + +impl<'a, 'b, C> QueueNameService for QueueNameClient<'a, 'b, C> +where + C: Client + Clone, +{ + fn queue_name(&self) -> &str { + self.queue_name.as_ref() + } + + fn put_message(&self) -> requests::PutMessageBuilder<'_, '_, Self::StorageClient, No> { + requests::PutMessageBuilder::new(self) + } +} diff --git a/azure_sdk_storage_queue/src/clients/queue_service_client.rs b/azure_sdk_storage_queue/src/clients/queue_service_client.rs new file mode 100644 index 000000000..5450057bc --- /dev/null +++ b/azure_sdk_storage_queue/src/clients/queue_service_client.rs @@ -0,0 +1,59 @@ +use crate::requests; +use crate::{HasStorageClient, IntoQueueServiceClient, QueueService, WithQueueServiceClient}; +use azure_sdk_storage_core::Client; +use std::borrow::Cow; +use std::fmt::Debug; + +#[derive(Debug, Clone)] +pub struct QueueServiceClient<'a, C> +where + C: Client + Clone, +{ + pub storage_client: Cow<'a, C>, +} + +impl<'a, C> HasStorageClient for QueueServiceClient<'a, C> +where + C: Client + Clone, +{ + type StorageClient = C; + + fn storage_client(&self) -> &C { + self.storage_client.as_ref() + } +} + +impl<'a, C> WithQueueServiceClient<'a> for C +where + C: Client + 'a + Clone, +{ + type QueueServiceClient = QueueServiceClient<'a, C>; + + fn with_queue_service_client(&'a self) -> Self::QueueServiceClient { + QueueServiceClient { + storage_client: Cow::Borrowed(self), + } + } +} + +impl IntoQueueServiceClient for C +where + C: Client + 'static + Clone, +{ + type QueueServiceClient = QueueServiceClient<'static, C>; + + fn into_queue_service_client(self) -> Self::QueueServiceClient { + QueueServiceClient { + storage_client: Cow::Owned(self), + } + } +} + +impl<'a, C> QueueService for QueueServiceClient<'a, C> +where + C: Client + Clone, +{ + fn list_queues(&self) -> requests::ListQueuesBuilder<'_, '_, Self::StorageClient> { + crate::requests::ListQueuesBuilder::new(self) + } +} diff --git a/azure_sdk_storage_queue/src/lib.rs b/azure_sdk_storage_queue/src/lib.rs new file mode 100644 index 000000000..850370742 --- /dev/null +++ b/azure_sdk_storage_queue/src/lib.rs @@ -0,0 +1,108 @@ +#![warn(unused_extern_crates)] +#![recursion_limit = "128"] +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate azure_sdk_core; + +pub mod prelude; +pub mod requests; +pub mod responses; +use azure_sdk_core::No; +use azure_sdk_storage_core::Client; +use core::fmt::Debug; +mod clients; +pub use clients::*; +use std::borrow::Cow; + +//********* Request traits +pub trait VisibilityTimeoutSupport { + type O; + fn with_visibility_timeout_seconds(self, timeout: u64) -> Self::O; +} + +pub trait VisibilityTimeoutRequired { + fn visibility_timeout_seconds(&self) -> u64; + + fn to_uri_parameter(&self) -> String { + format!("visibilitytimeout={}", self.visibility_timeout_seconds()) + } +} + +pub trait MessageTTLSupport { + type O; + fn with_message_ttl_seconds(self, timeout: u64) -> Self::O; +} + +pub trait MessageTTLRequired { + fn message_ttl_seconds(&self) -> u64; + + fn to_uri_parameter(&self) -> String { + format!("messagettl={}", self.message_ttl_seconds()) + } +} + +/// Wraps the message like: '\\{}\\' +/// as per Azure specification. +/// See +/// [https://docs.microsoft.com/en-us/rest/api/storageservices/put-message](https://docs.microsoft.com/en-us/rest/api/storageservices/put-message) +pub trait MessageBodySupport<'b> { + type O; + + /// Wraps the message like: '\\{}\\' + /// as per Azure specification. + /// See + /// [https://docs.microsoft.com/en-us/rest/api/storageservices/put-message](https://docs.microsoft.com/en-us/rest/api/storageservices/put-message) + fn with_message_body>>(self, body: BODY) -> Self::O; +} + +pub trait MessageBodyRequired { + fn message_body<'b>(&self) -> &str; +} + +//********* Queue service traits +pub trait HasStorageClient: Debug + Send + Sync { + type StorageClient: Client; + fn storage_client(&self) -> &Self::StorageClient; +} + +pub trait QueueService: HasStorageClient + Sync { + fn list_queues(&self) -> requests::ListQueuesBuilder<'_, '_, Self::StorageClient>; +} + +pub trait WithQueueServiceClient<'a>: Debug + Send + Sync { + type QueueServiceClient: QueueService; + + fn with_queue_service_client(&'a self) -> Self::QueueServiceClient; +} + +pub trait IntoQueueServiceClient: Debug + Send + Sync { + type QueueServiceClient: QueueService; + + fn into_queue_service_client(self) -> Self::QueueServiceClient; +} + +//************* +pub trait QueueNameService: HasStorageClient { + fn queue_name(&self) -> &str; + + fn put_message(&self) -> requests::PutMessageBuilder<'_, '_, Self::StorageClient, No>; +} + +pub trait WithQueueNameClient<'a, 'b>: Debug + Send + Sync { + type QueueNameClient: QueueNameService; + + fn with_queue_name_client(&'a self, queue_name: NAME) -> Self::QueueNameClient + where + NAME: Into>; +} + +pub trait IntoQueueNameClient<'b>: Debug + Send + Sync { + type QueueNameClient: QueueNameService; + + fn into_queue_name_client(self, queue_name: NAME) -> Self::QueueNameClient + where + NAME: Into>; +} diff --git a/azure_sdk_storage_queue/src/prelude.rs b/azure_sdk_storage_queue/src/prelude.rs new file mode 100644 index 000000000..3187e2d79 --- /dev/null +++ b/azure_sdk_storage_queue/src/prelude.rs @@ -0,0 +1,6 @@ +pub use crate::{ + IntoQueueNameClient, IntoQueueServiceClient, MessageBodyRequired, MessageBodySupport, + MessageTTLRequired, MessageTTLSupport, QueueNameService, QueueService, + VisibilityTimeoutRequired, VisibilityTimeoutSupport, WithQueueNameClient, + WithQueueServiceClient, +}; diff --git a/azure_sdk_storage_queue/src/requests/list_queues_builder.json b/azure_sdk_storage_queue/src/requests/list_queues_builder.json new file mode 100644 index 000000000..5114f7b12 --- /dev/null +++ b/azure_sdk_storage_queue/src/requests/list_queues_builder.json @@ -0,0 +1,67 @@ +{ + "name": "ListQueuesBuilder", + "derive": "Debug, Clone", + "uses": [ + "crate::prelude::*", + "crate::responses::*", + "azure_sdk_core::errors::{check_status_extract_headers_and_body, AzureError}", + "azure_sdk_core::prelude::*", + "azure_sdk_storage_core::prelude::*", + "hyper::StatusCode", + "std::convert::TryInto" + ], + "inline": true, + "extra_types": [ "'a", "'b", "C" ], + "extra_wheres": [ "C: Client" ], + "constructor_fields": [ + { + "name": "queue_service", + "field_type": "&'a dyn QueueService" + } + ], + "fields": [ + { + "name": "prefix", + "field_type": "&'b str", + "optional": true, + "trait_get": "PrefixOption<'b>", + "trait_set": "PrefixSupport<'b>" + }, + { + "name": "next_marker", + "field_type": "&'b str", + "optional": true, + "trait_get": "NextMarkerOption<'b>", + "trait_set": "NextMarkerSupport<'b>" + }, + { + "name": "max_results", + "field_type": "u32", + "optional": true, + "trait_get": "MaxResultsOption", + "trait_set": "MaxResultsSupport" + }, + { + "name": "include_metadata", + "field_type": "bool", + "optional": true, + "initializer": "false", + "trait_get": "IncludeMetadataOption", + "trait_set": "IncludeMetadataSupport" + }, + { + "name": "timeout", + "field_type": "u64", + "optional": true, + "trait_get": "TimeoutOption", + "trait_set": "TimeoutSupport" + }, + { + "name": "client_request_id", + "field_type": "&'a str", + "optional": true, + "trait_get": "ClientRequestIdOption<'a>", + "trait_set": "ClientRequestIdSupport<'a>" + } + ] +} diff --git a/azure_sdk_storage_queue/src/requests/list_queues_builder.rs b/azure_sdk_storage_queue/src/requests/list_queues_builder.rs new file mode 100644 index 000000000..b9d907951 --- /dev/null +++ b/azure_sdk_storage_queue/src/requests/list_queues_builder.rs @@ -0,0 +1,278 @@ +use crate::prelude::*; +use crate::responses::*; +use azure_sdk_core::errors::{check_status_extract_headers_and_body, AzureError}; +use azure_sdk_core::prelude::*; +use azure_sdk_storage_core::prelude::*; +use hyper::StatusCode; +use std::convert::TryInto; + +#[derive(Debug, Clone)] +pub struct ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + queue_service: &'a dyn QueueService, + prefix: Option<&'b str>, + next_marker: Option<&'b str>, + max_results: Option, + include_metadata: bool, + timeout: Option, + client_request_id: Option<&'a str>, +} + +impl<'a, 'b, C> ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + pub(crate) fn new( + queue_service: &'a dyn QueueService, + ) -> ListQueuesBuilder<'a, 'b, C> { + ListQueuesBuilder { + queue_service, + prefix: None, + next_marker: None, + max_results: None, + include_metadata: false, + timeout: None, + client_request_id: None, + } + } +} + +//set mandatory no traits methods +impl<'a, 'b, C> PrefixOption<'b> for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + fn prefix(&self) -> Option<&'b str> { + self.prefix + } +} + +impl<'a, 'b, C> NextMarkerOption<'b> for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + fn next_marker(&self) -> Option<&'b str> { + self.next_marker + } +} + +impl<'a, 'b, C> MaxResultsOption for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + fn max_results(&self) -> Option { + self.max_results + } +} + +impl<'a, 'b, C> IncludeMetadataOption for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + fn include_metadata(&self) -> bool { + self.include_metadata + } +} + +impl<'a, 'b, C> TimeoutOption for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + fn timeout(&self) -> Option { + self.timeout + } +} + +impl<'a, 'b, C> ClientRequestIdOption<'a> for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + #[inline] + fn client_request_id(&self) -> Option<&'a str> { + self.client_request_id + } +} + +impl<'a, 'b, C> PrefixSupport<'b> for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + type O = ListQueuesBuilder<'a, 'b, C>; + + #[inline] + fn with_prefix(self, prefix: &'b str) -> Self::O { + ListQueuesBuilder { + queue_service: self.queue_service, + prefix: Some(prefix), + next_marker: self.next_marker, + max_results: self.max_results, + include_metadata: self.include_metadata, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C> NextMarkerSupport<'b> for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + type O = ListQueuesBuilder<'a, 'b, C>; + + #[inline] + fn with_next_marker(self, next_marker: &'b str) -> Self::O { + ListQueuesBuilder { + queue_service: self.queue_service, + prefix: self.prefix, + next_marker: Some(next_marker), + max_results: self.max_results, + include_metadata: self.include_metadata, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C> MaxResultsSupport for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + type O = ListQueuesBuilder<'a, 'b, C>; + + #[inline] + fn with_max_results(self, max_results: u32) -> Self::O { + ListQueuesBuilder { + queue_service: self.queue_service, + prefix: self.prefix, + next_marker: self.next_marker, + max_results: Some(max_results), + include_metadata: self.include_metadata, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C> IncludeMetadataSupport for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + type O = ListQueuesBuilder<'a, 'b, C>; + + #[inline] + fn with_include_metadata(self) -> Self::O { + ListQueuesBuilder { + queue_service: self.queue_service, + prefix: self.prefix, + next_marker: self.next_marker, + max_results: self.max_results, + include_metadata: true, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C> TimeoutSupport for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + type O = ListQueuesBuilder<'a, 'b, C>; + + #[inline] + fn with_timeout(self, timeout: u64) -> Self::O { + ListQueuesBuilder { + queue_service: self.queue_service, + prefix: self.prefix, + next_marker: self.next_marker, + max_results: self.max_results, + include_metadata: self.include_metadata, + timeout: Some(timeout), + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C> ClientRequestIdSupport<'a> for ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + type O = ListQueuesBuilder<'a, 'b, C>; + + #[inline] + fn with_client_request_id(self, client_request_id: &'a str) -> Self::O { + ListQueuesBuilder { + queue_service: self.queue_service, + prefix: self.prefix, + next_marker: self.next_marker, + max_results: self.max_results, + include_metadata: self.include_metadata, + timeout: self.timeout, + client_request_id: Some(client_request_id), + } + } +} + +// methods callable regardless +impl<'a, 'b, C> ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + pub fn queue_service(&self) -> &'a dyn QueueService { + self.queue_service + } +} + +// methods callable only when every mandatory field has been filled +impl<'a, 'b, C> ListQueuesBuilder<'a, 'b, C> +where + C: Client, +{ + pub async fn execute(self) -> Result { + let mut uri = format!( + "{}?comp=list", + self.queue_service.storage_client().queue_uri() + ); + + if let Some(nm) = IncludeMetadataOption::to_uri_parameter(&self) { + uri = format!("{}&{}", uri, nm); + } + if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { + uri = format!("{}&{}", uri, nm); + } + if let Some(nm) = MaxResultsOption::to_uri_parameter(&self) { + uri = format!("{}&{}", uri, nm); + } + if let Some(nm) = NextMarkerOption::to_uri_parameter(&self) { + uri = format!("{}&{}", uri, nm); + } + if let Some(nm) = PrefixOption::to_uri_parameter(&self) { + uri = format!("{}&{}", uri, nm); + } + + debug!("uri == {}", uri); + + let future_response = self.queue_service.storage_client().perform_request( + &uri, + &http::Method::GET, + &|mut request| { + request = ClientRequestIdOption::add_header(&self, request); + request + }, + Some(&[]), + )?; + + let (headers, body) = + check_status_extract_headers_and_body(future_response, StatusCode::OK).await?; + + (&headers, &body as &[u8]).try_into() + } +} diff --git a/azure_sdk_storage_queue/src/requests/mod.rs b/azure_sdk_storage_queue/src/requests/mod.rs new file mode 100644 index 000000000..b230d8825 --- /dev/null +++ b/azure_sdk_storage_queue/src/requests/mod.rs @@ -0,0 +1,4 @@ +mod put_message_builder; +pub use put_message_builder::PutMessageBuilder; +mod list_queues_builder; +pub use list_queues_builder::ListQueuesBuilder; diff --git a/azure_sdk_storage_queue/src/requests/put_message_builder.json b/azure_sdk_storage_queue/src/requests/put_message_builder.json new file mode 100644 index 000000000..e6c661045 --- /dev/null +++ b/azure_sdk_storage_queue/src/requests/put_message_builder.json @@ -0,0 +1,65 @@ +{ + "name": "PutMessageBuilder", + "derive": "Debug, Clone", + "uses": [ + "crate::prelude::*", + "crate::responses::*", + "azure_sdk_core::errors::{check_status_extract_headers_and_body, AzureError}", + "azure_sdk_core::prelude::*", + "azure_sdk_storage_core::prelude::*", + "std::marker::PhantomData", + "std::borrow::Cow", + "azure_sdk_core::{Yes,No,ToAssign}", + "hyper::StatusCode", + "std::convert::TryInto" + ], + "inline": true, + "extra_types": [ "'a", "'b", "C" ], + "extra_wheres": [ "C: Client" ], + "constructor_fields": [ + { + "name": "queue_name_service", + "field_type": "&'a dyn QueueNameService" + } + ], + "fields": [ + { + "name": "message_body", + "field_type": "Cow<'b, str>", + "builder_type": "MessageBodySet", + "optional": false, + "trait_get": "MessageBodyRequired", + "trait_set": "MessageBodySupport<'b>" + }, + { + "name": "visibility_timeout_seconds", + "field_type": "u64", + "optional": true, + "initializer": "0", + "trait_get": "VisibilityTimeoutRequired", + "trait_set": "VisibilityTimeoutSupport" + }, + { + "name": "message_ttl_seconds", + "field_type": "u64", + "optional": true, + "initializer": "25200", + "trait_get": "MessageTTLRequired", + "trait_set": "MessageTTLSupport" + }, + { + "name": "timeout", + "field_type": "u64", + "optional": true, + "trait_get": "TimeoutOption", + "trait_set": "TimeoutSupport" + }, + { + "name": "client_request_id", + "field_type": "&'a str", + "optional": true, + "trait_get": "ClientRequestIdOption<'a>", + "trait_set": "ClientRequestIdSupport<'a>" + } + ] +} diff --git a/azure_sdk_storage_queue/src/requests/put_message_builder.rs b/azure_sdk_storage_queue/src/requests/put_message_builder.rs new file mode 100644 index 000000000..016be7e93 --- /dev/null +++ b/azure_sdk_storage_queue/src/requests/put_message_builder.rs @@ -0,0 +1,268 @@ +use crate::prelude::*; +use crate::responses::*; +use azure_sdk_core::errors::{check_status_extract_headers_and_body, AzureError}; +use azure_sdk_core::prelude::*; +use azure_sdk_core::{No, ToAssign, Yes}; +use azure_sdk_storage_core::prelude::*; +use hyper::StatusCode; +use std::borrow::Cow; +use std::convert::TryInto; +use std::marker::PhantomData; + +#[derive(Debug, Clone)] +pub struct PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + queue_name_service: &'a dyn QueueNameService, + p_message_body: PhantomData, + message_body: Option>, + visibility_timeout_seconds: u64, + message_ttl_seconds: u64, + timeout: Option, + client_request_id: Option<&'a str>, +} + +impl<'a, 'b, C> PutMessageBuilder<'a, 'b, C, No> +where + C: Client, +{ + #[inline] + pub(crate) fn new( + queue_name_service: &'a dyn QueueNameService, + ) -> PutMessageBuilder<'a, 'b, C, No> { + PutMessageBuilder { + queue_name_service, + p_message_body: PhantomData {}, + message_body: None, + visibility_timeout_seconds: 0, + message_ttl_seconds: 25200, + timeout: None, + client_request_id: None, + } + } +} + +//set mandatory no traits methods +impl<'a, 'b, C> MessageBodyRequired for PutMessageBuilder<'a, 'b, C, Yes> +where + C: Client, +{ + #[inline] + fn message_body(&self) -> &str { + self.message_body.as_ref().unwrap() + } +} + +impl<'a, 'b, C, MessageBodySet> VisibilityTimeoutRequired + for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + #[inline] + fn visibility_timeout_seconds(&self) -> u64 { + self.visibility_timeout_seconds + } +} + +impl<'a, 'b, C, MessageBodySet> MessageTTLRequired for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + #[inline] + fn message_ttl_seconds(&self) -> u64 { + self.message_ttl_seconds + } +} + +impl<'a, 'b, C, MessageBodySet> TimeoutOption for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + #[inline] + fn timeout(&self) -> Option { + self.timeout + } +} + +impl<'a, 'b, C, MessageBodySet> ClientRequestIdOption<'a> + for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + #[inline] + fn client_request_id(&self) -> Option<&'a str> { + self.client_request_id + } +} + +impl<'a, 'b, C> MessageBodySupport<'b> for PutMessageBuilder<'a, 'b, C, No> +where + C: Client, +{ + type O = PutMessageBuilder<'a, 'b, C, Yes>; + + #[inline] + fn with_message_body>>(self, message_body: BODY) -> Self::O { + PutMessageBuilder { + queue_name_service: self.queue_name_service, + p_message_body: PhantomData {}, + message_body: Some(message_body.into()), + visibility_timeout_seconds: self.visibility_timeout_seconds, + message_ttl_seconds: self.message_ttl_seconds, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C, MessageBodySet> VisibilityTimeoutSupport + for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + type O = PutMessageBuilder<'a, 'b, C, MessageBodySet>; + + #[inline] + fn with_visibility_timeout_seconds(self, visibility_timeout_seconds: u64) -> Self::O { + PutMessageBuilder { + queue_name_service: self.queue_name_service, + p_message_body: PhantomData {}, + message_body: self.message_body, + visibility_timeout_seconds, + message_ttl_seconds: self.message_ttl_seconds, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C, MessageBodySet> MessageTTLSupport for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + type O = PutMessageBuilder<'a, 'b, C, MessageBodySet>; + + #[inline] + fn with_message_ttl_seconds(self, message_ttl_seconds: u64) -> Self::O { + PutMessageBuilder { + queue_name_service: self.queue_name_service, + p_message_body: PhantomData {}, + message_body: self.message_body, + visibility_timeout_seconds: self.visibility_timeout_seconds, + message_ttl_seconds, + timeout: self.timeout, + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C, MessageBodySet> TimeoutSupport for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + type O = PutMessageBuilder<'a, 'b, C, MessageBodySet>; + + #[inline] + fn with_timeout(self, timeout: u64) -> Self::O { + PutMessageBuilder { + queue_name_service: self.queue_name_service, + p_message_body: PhantomData {}, + message_body: self.message_body, + visibility_timeout_seconds: self.visibility_timeout_seconds, + message_ttl_seconds: self.message_ttl_seconds, + timeout: Some(timeout), + client_request_id: self.client_request_id, + } + } +} + +impl<'a, 'b, C, MessageBodySet> ClientRequestIdSupport<'a> + for PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + type O = PutMessageBuilder<'a, 'b, C, MessageBodySet>; + + #[inline] + fn with_client_request_id(self, client_request_id: &'a str) -> Self::O { + PutMessageBuilder { + queue_name_service: self.queue_name_service, + p_message_body: PhantomData {}, + message_body: self.message_body, + visibility_timeout_seconds: self.visibility_timeout_seconds, + message_ttl_seconds: self.message_ttl_seconds, + timeout: self.timeout, + client_request_id: Some(client_request_id), + } + } +} + +// methods callable regardless +impl<'a, 'b, C, MessageBodySet> PutMessageBuilder<'a, 'b, C, MessageBodySet> +where + MessageBodySet: ToAssign, + C: Client, +{ + pub fn queue_name_service(&self) -> &'a dyn QueueNameService { + self.queue_name_service + } +} + +// methods callable only when every mandatory field has been filled +impl<'a, 'b, C> PutMessageBuilder<'a, 'b, C, Yes> +where + C: Client, +{ + pub async fn execute(self) -> Result { + let mut uri = format!( + "{}/{}/messages", + self.queue_name_service.storage_client().queue_uri(), + self.queue_name_service.queue_name() + ); + + uri = format!( + "{}?{}", + uri, + VisibilityTimeoutRequired::to_uri_parameter(&self) + ); + uri = format!("{}&{}", uri, MessageTTLRequired::to_uri_parameter(&self)); + if let Some(nm) = TimeoutOption::to_uri_parameter(&self) { + uri = format!("{}&{}", uri, nm); + } + + debug!("uri == {}", uri); + + // since the format is fixed we just decorate the message with the tags. + // This could be made optional in the future and/or more + // stringent. + let message = format!( + "{}", + self.message_body() + ); + + let future_response = self.queue_name_service.storage_client().perform_request( + &uri, + &http::Method::POST, + &|mut request| { + request = ClientRequestIdOption::add_header(&self, request); + request + }, + Some(message.as_bytes()), + )?; + + let (headers, body) = + check_status_extract_headers_and_body(future_response, StatusCode::CREATED).await?; + + (&headers, &body as &[u8]).try_into() + } +} diff --git a/azure_sdk_storage_queue/src/responses/list_queues_response.rs b/azure_sdk_storage_queue/src/responses/list_queues_response.rs new file mode 100644 index 000000000..6bb142dc9 --- /dev/null +++ b/azure_sdk_storage_queue/src/responses/list_queues_response.rs @@ -0,0 +1,106 @@ +use azure_sdk_core::errors::AzureError; +use azure_sdk_core::CommonStorageResponseHeaders; +use hyper::header::HeaderMap; +use std::convert::TryInto; + +#[derive(Debug, Clone)] +pub struct ListQueuesResponse { + pub common_storage_response_headers: CommonStorageResponseHeaders, + pub service_endpoint: String, + pub prefix: Option, + pub marker: Option, + pub max_results: Option, + + pub queues: Queues, + + pub next_marker: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct ListQueuesResponseInternal { + #[serde(rename = "ServiceEndpoint")] + pub service_endpoint: String, + #[serde(rename = "Prefix")] + pub prefix: Option, + #[serde(rename = "Marker")] + pub marker: Option, + #[serde(rename = "MaxResults")] + pub max_results: Option, + + #[serde(rename = "Queues")] + pub queues: Queues, + + #[serde(rename = "NextMarker")] + pub next_marker: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Queues { + #[serde(rename = "Queue")] + pub queues: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Queue { + #[serde(rename = "Name")] + pub name: String, + #[serde(rename = "Metadata")] + pub metadata: Option>, +} + +impl std::convert::TryFrom<(&HeaderMap, &[u8])> for ListQueuesResponse { + type Error = AzureError; + fn try_from(value: (&HeaderMap, &[u8])) -> Result { + let headers = value.0; + let body = value.1; + + println!("headers == {:?}", headers); + + let received = &std::str::from_utf8(body)?[3..]; + println!("receieved == {:#?}", received); + let mut response: ListQueuesResponseInternal = serde_xml_rs::from_reader(&body[3..])?; + + // get rid of the ugly Some("") empty string + // we use None as Rust dictates to identify + // lack of value. + if let Some(next_marker) = &response.next_marker { + if next_marker == "" { + response.next_marker = None; + } + } + + // get rid of the ugly metadata: Some( {} ) in case of + // no metadata returned. + response.queues.queues.iter_mut().for_each(|queue| { + if let Some(metadata) = &queue.metadata { + if metadata.is_empty() { + queue.metadata = None; + } + } + }); + + Ok(ListQueuesResponse { + common_storage_response_headers: headers.try_into()?, + service_endpoint: response.service_endpoint, + prefix: response.prefix, + marker: response.marker, + max_results: response.max_results, + queues: response.queues, + next_marker: response.next_marker, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn try_parse() { + let range = "a2azureiscoolazurerocks"; + + let response: ListQueuesResponseInternal = serde_xml_rs::from_str(range).unwrap(); + + assert_eq!(response.queues.queues.len(), 2); + } +} diff --git a/azure_sdk_storage_queue/src/responses/mod.rs b/azure_sdk_storage_queue/src/responses/mod.rs new file mode 100644 index 000000000..9f8147632 --- /dev/null +++ b/azure_sdk_storage_queue/src/responses/mod.rs @@ -0,0 +1,4 @@ +mod list_queues_response; +pub use list_queues_response::ListQueuesResponse; +mod put_message_response; +pub use put_message_response::PutMessageResponse; diff --git a/azure_sdk_storage_queue/src/responses/put_message_response.rs b/azure_sdk_storage_queue/src/responses/put_message_response.rs new file mode 100644 index 000000000..ba4fe4b42 --- /dev/null +++ b/azure_sdk_storage_queue/src/responses/put_message_response.rs @@ -0,0 +1,67 @@ +use azure_sdk_core::errors::AzureError; +use azure_sdk_core::{utc_date_from_rfc2822, CommonStorageResponseHeaders}; +use chrono::{DateTime, Utc}; +use hyper::header::HeaderMap; +use std::convert::TryInto; + +#[derive(Debug, Clone)] +pub struct PutMessageResponse { + pub common_storage_response_headers: CommonStorageResponseHeaders, + pub queue_message: QueueMessage, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct PutMessageResponseInternal { + #[serde(rename = "QueueMessage")] + pub queue_message: QueueMessageInternal, +} + +#[derive(Debug, Clone)] +pub struct QueueMessage { + pub message_id: String, + pub insertion_time: DateTime, + pub expiration_time: DateTime, + pub pop_receipt: String, + pub time_next_visible: DateTime, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct QueueMessageInternal { + #[serde(rename = "MessageId")] + pub message_id: String, + #[serde(rename = "InsertionTime")] + pub insertion_time: String, + #[serde(rename = "ExpirationTime")] + pub expiration_time: String, + #[serde(rename = "PopReceipt")] + pub pop_receipt: String, + #[serde(rename = "TimeNextVisible")] + pub time_next_visible: String, +} + +impl std::convert::TryFrom<(&HeaderMap, &[u8])> for PutMessageResponse { + type Error = AzureError; + fn try_from(value: (&HeaderMap, &[u8])) -> Result { + let headers = value.0; + let body = value.1; + + debug!("headers == {:?}", headers); + + let received = &std::str::from_utf8(body)?[3..]; + debug!("receieved == {:#?}", received); + let response: PutMessageResponseInternal = serde_xml_rs::from_reader(&body[3..])?; + + let queue_message = QueueMessage { + message_id: response.queue_message.message_id, + insertion_time: utc_date_from_rfc2822(&response.queue_message.insertion_time)?, + expiration_time: utc_date_from_rfc2822(&response.queue_message.expiration_time)?, + pop_receipt: response.queue_message.pop_receipt, + time_next_visible: utc_date_from_rfc2822(&response.queue_message.time_next_visible)?, + }; + + Ok(Self { + common_storage_response_headers: headers.try_into()?, + queue_message, + }) + } +} diff --git a/scripts/publish_all.sh b/scripts/publish_all.sh index fb84557ec..4e8dbd0f2 100755 --- a/scripts/publish_all.sh +++ b/scripts/publish_all.sh @@ -16,8 +16,6 @@ cd azure_sdk_storage_account cargo publish cd .. -sleep 20 - cd azure_sdk_storage_blob cargo publish cd .. @@ -26,6 +24,10 @@ cd azure_sdk_storage_table cargo publish cd .. +cd azure_sdk_storage_queue +cargo publish +cd .. + cd azure_sdk_auth_aad cargo publish cd ..