builder pattern for container (uncompleted)

This commit is contained in:
Francesco Cogno 2018-06-17 13:23:29 +02:00
Родитель afcfc120ba
Коммит bdb33a8727
23 изменённых файлов: 1094 добавлений и 187 удалений

4
.gitignore поставляемый
Просмотреть файл

@ -7,4 +7,6 @@ sample/
\#*\#
\.#*
*~
*.swp
*.swp
*.vim

Просмотреть файл

@ -1,6 +1,6 @@
[package]
name = "azure_sdk_for_rust"
version = "0.8.0"
version = "0.9.0"
description = "Rust wrappers around Microsoft Azure REST APIs"
readme = "README.md"
authors = ["Francesco Cogno <francesco.cogno@outlook.com>", "Max Gortman <mgortman@microsoft.com>", "Dong Liu <doliu@microsoft.com>"]

Просмотреть файл

@ -1,20 +1,16 @@
extern crate azure_sdk_for_rust;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate tokio_core;
use std::error::Error;
use futures::future::*;
use tokio_core::reactor::Core;
use azure_sdk_for_rust::storage::{
blob::{Blob, LIST_BLOB_OPTIONS_DEFAULT},
client::Client,
container::{Container, LIST_CONTAINER_OPTIONS_DEFAULT},
};
use futures::future::*;
use std::error::Error;
use tokio_core::reactor::Core;
fn main() {
code().unwrap();
@ -35,12 +31,15 @@ fn code() -> Result<(), Box<Error>> {
let client = Client::new(&account, &master_key)?;
let future = Container::list(&client, &LIST_CONTAINER_OPTIONS_DEFAULT).map(|iv| {
println!("List containers returned {} containers.", iv.len());
for cont in iv.iter() {
println!("\t{}", cont.name);
}
});
let future = {
use azure_sdk_for_rust::storage::client::Container;
client.list().finalize().map(|iv| {
println!("List containers returned {} containers.", iv.len());
for cont in iv.iter() {
println!("\t{}", cont.name);
}
})
};
core.run(future)?;

69
examples/container01.rs Normal file
Просмотреть файл

@ -0,0 +1,69 @@
extern crate azure_sdk_for_rust;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate tokio_core;
use azure_sdk_for_rust::core::{ClientRequestIdSupport, TimeoutSupport};
use azure_sdk_for_rust::storage::{
client::Client,
container::{PublicAccess, PublicAccessSupport},
};
use futures::Future;
use std::collections::HashMap;
use std::error::Error;
use tokio_core::reactor::Core;
fn main() {
code().unwrap();
}
// We run a separate method to use the elegant question mark operator.
// A series of unwrap(), unwrap() would have achieved the same result.
fn code() -> Result<(), Box<Error>> {
// 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 container_name = std::env::args()
.nth(1)
.expect("please specify container name as command line parameter");
let mut core = Core::new()?;
let client = Client::new(&account, &master_key)?;
use azure_sdk_for_rust::storage::client::Container;
let future = client.list().with_client_request_id("ciccio").include_metadata().finalize();
core.run(future.map(|res| {
println!("{:?}", res);
}))?;
let mut metadata = HashMap::new();
metadata.insert("prova", "pollo");
metadata.insert("canotto", "cigno");
// This is the builder pattern. Notice two things:
// 1 - The various parameters are clearly defined.
// 2 - If you forget a mandatory parameter the code won't compile. Type checking at compile
// time is waaay better than doing it at runtime!
let future = client
.create()
.with_container_name(&container_name)
.with_public_access(PublicAccess::None)
.with_metadata(metadata)
.with_timeout(100)
.finalize();
core.run(future)?;
let future = client.delete().with_container_name(&container_name).finalize();
core.run(future).map(|_| {
println!("container {} deleted!", container_name);
})?;
Ok(())
}

Просмотреть файл

@ -36,6 +36,16 @@ macro_rules! create_enum {
}
}
impl AsRef<str> for $en {
fn as_ref(&self) -> &str {
match *self {
$(
$en::$na => $x,
)*
}
}
}
impl fmt::Display for $en {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {

Просмотреть файл

@ -96,12 +96,15 @@ quick_error! {
display("XML error: {}", err)
cause(err)
}
UnexpectedXMLError(err: String) {
display("UnexpectedXMLError: {}", err)
}
AzurePathParseError(err: AzurePathParseError){
from()
display("Azure Path parse error: {}", err)
cause(err)
}
UnexpectedHTTPResult(err: UnexpectedHTTPResult){
UnexpectedHTTPResult(err: UnexpectedHTTPResult){
from()
display("UnexpectedHTTPResult error")
}
@ -188,6 +191,9 @@ quick_error! {
EnumerationNotMatched(msg: String) {
display("Enumeration not matched: {}", msg)
}
BooleanNotMatched(s: String) {
display("Input string cannot be converted in boolean: {}", s)
}
DateTimeParseError(err: chrono::format::ParseError){
from()
display("DateTime parse error: {}", err)

Просмотреть файл

@ -0,0 +1,3 @@
pub const LEASE_ID: &str = "x-ms-lease-id"; //=> [LeaseId] }
pub const CLIENT_REQUEST_ID: &str = "x-ms-client-request-id"; //=> [String] }
pub const BLOB_PUBLIC_ACCESS: &str = "x-ms-blob-public-access"; // [PublicAccess]

Просмотреть файл

@ -9,18 +9,120 @@ pub mod parsing;
pub mod enumerations;
pub mod incompletevector;
pub mod lease;
use azure::storage::client::Client;
use std::fmt::Debug;
pub mod ba512_range;
pub mod range;
use url::percent_encoding;
define_encode_set! {
pub COMPLETE_ENCODE_SET = [percent_encoding::USERINFO_ENCODE_SET] | {
'+', '-', '&'
}
}
pub mod headers;
use self::headers::{CLIENT_REQUEST_ID, LEASE_ID};
use uuid::Uuid;
pub type RequestId = Uuid;
use azure::core::lease::LeaseId;
use http::request::Builder;
pub(crate) mod util;
#[derive(Debug)]
pub struct Yes;
#[derive(Debug)]
pub struct No;
pub trait ToAssign: Debug {}
pub trait Assigned: ToAssign {}
pub trait NotAssigned: ToAssign {}
impl ToAssign for Yes {}
impl ToAssign for No {}
impl Assigned for Yes {}
impl NotAssigned for No {}
pub trait ClientRequired<'a> {
fn client(&self) -> &'a Client;
}
pub trait TimeoutSupport {
type O;
fn with_timeout(self, timeout: u64) -> Self::O;
}
pub trait TimeoutOption {
fn timeout(&self) -> Option<u64>;
fn to_uri_parameter(&self) -> Option<String> {
if let Some(nm) = self.timeout() {
Some(format!("timeout={}", nm))
} else {
None
}
}
}
pub trait ClientRequestIdSupport<'a> {
type O;
fn with_client_request_id(self, client_request_id: &'a str) -> Self::O;
}
pub trait ClientRequestIdOption<'a> {
fn client_request_id(&self) -> Option<&'a str>;
fn add_header(&self, builder: &mut Builder) {
if let Some(client_request_id) = self.client_request_id() {
builder.header(CLIENT_REQUEST_ID, client_request_id);
}
}
}
pub trait NextMarkerSupport<'a> {
type O;
fn with_next_marker(self, next_marker: &'a str) -> Self;
}
pub trait NextMarkerOption<'a> {
fn next_marker(&self) -> Option<&'a str>;
fn to_uri_parameter(&self) -> Option<String> {
if let Some(ref nm) = self.next_marker() {
Some(format!("marker={}", nm))
} else {
None
}
}
}
pub trait PrefixSupport<'a> {
type O;
fn with_prefix(self, prefix: &'a str) -> Self::O;
}
pub trait PrefixOption<'a> {
fn prefix(&self) -> Option<&'a str>;
fn to_uri_parameter(&self) -> Option<String> {
if let Some(ref nm) = self.prefix() {
Some(format!("prefix={}", nm))
} else {
None
}
}
}
pub trait LeaseIdSupport<'a> {
type O;
fn with_lease_id(self, &'a LeaseId) -> Self::O;
}
pub trait LeaseIdOption<'a> {
fn lease_id(&self) -> Option<&'a LeaseId>;
fn add_header(&self, builder: &mut Builder) {
if let Some(lease_id) = self.lease_id() {
builder.header(LEASE_ID, &lease_id.to_string() as &str);
}
}
}

Просмотреть файл

@ -19,6 +19,16 @@ impl FromStringOptional<String> for String {
}
}
impl FromStringOptional<bool> for bool {
fn from_str_optional(s: &str) -> Result<bool, TraversingError> {
match s {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(TraversingError::BooleanNotMatched(String::from(s))),
}
}
}
impl FromStringOptional<chrono::DateTime<chrono::Utc>> for chrono::DateTime<chrono::Utc> {
fn from_str_optional(s: &str) -> Result<chrono::DateTime<chrono::Utc>, TraversingError> {
match from_azure_time(s) {

Просмотреть файл

@ -25,6 +25,7 @@ fn format_as_bytes<D: Display>(value: D) -> Bytes {
wrt.0.freeze()
}
#[allow(dead_code)]
pub fn into_header_value<B: Into<Bytes>>(value: B) -> Result<HeaderValue, http::Error> {
let value = value.into();
Ok(HeaderValue::try_from(value)?)

Просмотреть файл

@ -34,6 +34,7 @@ pub use self::block_list::BlockList;
mod get_block_list_response;
pub use self::get_block_list_response::GetBlockListResponse;
use azure::core::headers::{CLIENT_REQUEST_ID, LEASE_ID};
use base64;
use chrono::{DateTime, Utc};
use futures::{future::*, prelude::*};
@ -75,6 +76,7 @@ create_enum!(PageWriteType, (Update, "update"), (Clear, "clear"));
const HEADER_BLOB_CONTENT_LENGTH: &str = "x-ms-blob-content-length";
const HEADER_BLOB_SEQUENCE_NUMBER: &str = "x-ms-blob-sequence-number";
const HEADER_BLOB_TYPE: &str = "x-ms-blob-type";
#[allow(dead_code)]
const HEADER_BLOB_CONTENT_DISPOSITION: &str = "x-ms-blob-content-disposition";
const HEADER_PAGE_WRITE: &str = "x-ms-blob-page-write";
@ -361,7 +363,7 @@ impl Blob {
}
}
if let Some(l) = lease_id {
request.header_formatted(HEADER_LEASE_ID, l);
request.header_formatted(LEASE_ID, l);
}
},
None,
@ -451,7 +453,7 @@ impl Blob {
request.header_formatted(HEADER_BLOB_TYPE, self.blob_type);
if let Some(ref lease_id) = po.lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
}
// TODO x-ms-blob-content-disposition
@ -489,7 +491,7 @@ impl Blob {
Method::PUT,
move |ref mut request| {
if let Some(ref lease_id) = lbo.lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
}
request.header_formatted(HEADER_LEASE_ACTION, la);
@ -504,7 +506,7 @@ impl Blob {
request.header_formatted(HEADER_PROPOSED_LEASE_ID, proposed_lease_id);
}
if let Some(ref request_id) = lbo.request_id {
request.header_formatted(HEADER_CLIENT_REQUEST_ID, request_id);
request.header_formatted(CLIENT_REQUEST_ID, request_id);
}
},
// this fix is needed to avoid
@ -524,7 +526,7 @@ impl Blob {
.and_then(move |future_response| check_status_extract_headers_and_body(future_response, expected_result))
.and_then(|(headers, _)| {
headers
.get_as_str(HEADER_LEASE_ID)
.get_as_str(LEASE_ID)
.and_then(|s| s.parse::<Uuid>().ok())
.ok_or_else(|| AzureError::HeaderNotFound("x-ms-lease-id".to_owned()))
})
@ -556,7 +558,7 @@ impl Blob {
request.header_formatted(HEADER_RANGE, range);
request.header_formatted(HEADER_BLOB_CONTENT_LENGTH, content.len());
if let Some(lease_id) = ppo.lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
}
request.header_formatted(HEADER_PAGE_WRITE, PageWriteType::Update);
@ -625,7 +627,7 @@ impl Blob {
request.header_formatted(HEADER_BLOB_TYPE, self.blob_type);
if let Some(ref lease_id) = pbo.lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
}
// TODO x-ms-blob-content-disposition
@ -635,7 +637,7 @@ impl Blob {
}
if let Some(ref request_id) = pbo.request_id {
request.header_formatted(HEADER_CLIENT_REQUEST_ID, request_id.to_owned());
request.header_formatted(CLIENT_REQUEST_ID, request_id.to_owned());
}
},
Some(content),
@ -672,7 +674,7 @@ impl Blob {
request.header_formatted(HEADER_RANGE, Range::from(range));
request.header_static(HEADER_BLOB_CONTENT_LENGTH, "0");
if let Some(lease_id) = lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
}
request.header_formatted(HEADER_PAGE_WRITE, PageWriteType::Clear);
@ -699,7 +701,7 @@ impl Blob {
Method::DELETE,
|ref mut request| {
if let Some(lease_id) = lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
}
},
None,
@ -757,7 +759,7 @@ where
request.header_formatted(header::CONTENT_LENGTH, xml_bytes.len());
request.header_formatted(HEADER_CONTENT_MD5, md5);
if let Some(lease_id) = lease_id {
request.header_formatted(HEADER_LEASE_ID, *lease_id);
request.header_formatted(LEASE_ID, *lease_id);
}
},
Some(xml_bytes),
@ -821,10 +823,10 @@ where
Method::GET,
move |ref mut request| {
if let Some(lease_id) = lease_id {
request.header_formatted(HEADER_LEASE_ID, lease_id);
request.header_formatted(LEASE_ID, lease_id);
};
if let Some(request_id) = request_id {
request.header_bytes(HEADER_CLIENT_REQUEST_ID, request_id);
request.header_bytes(CLIENT_REQUEST_ID, request_id);
};
},
None,

Просмотреть файл

@ -1,5 +1,7 @@
use super::rest_client::{perform_request, ServiceType};
use azure::core::errors::AzureError;
use azure::core::No;
use azure::storage::container;
use hyper::{self, Method};
use hyper_tls;
@ -7,12 +9,33 @@ use hyper_tls;
const SERVICE_SUFFIX_BLOB: &str = ".blob.core.windows.net";
const SERVICE_SUFFIX_TABLE: &str = ".table.core.windows.net";
pub trait Container {
fn create<'a>(&'a self) -> container::requests::CreateBuilder<'a, No, No>;
fn delete<'a>(&'a self) -> container::requests::DeleteBuilder<'a, No>;
fn list<'a>(&'a self) -> container::requests::ListBuilder<'a>;
}
#[derive(Debug)]
pub struct Client {
account: String,
key: String,
hc: hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>,
}
impl Container for Client {
fn create<'a>(&'a self) -> container::requests::CreateBuilder<'a, No, No> {
container::requests::CreateBuilder::new(self)
}
fn delete<'a>(&'a self) -> container::requests::DeleteBuilder<'a, No> {
container::requests::DeleteBuilder::new(self)
}
fn list<'a>(&'a self) -> container::requests::ListBuilder<'a> {
container::requests::ListBuilder::new(self)
}
}
impl Client {
pub fn new(account: &str, key: &str) -> Result<Client, AzureError> {
use hyper;

Просмотреть файл

@ -0,0 +1,8 @@
pub struct CreateRequest<N> {
name: String,
timeout: Option<u64>
}
impl CreateRequest

Просмотреть файл

@ -1,16 +0,0 @@
#[derive(Debug, Clone, PartialEq)]
pub struct ListContainerOptions {
pub max_results: u32,
pub include_metadata: bool,
pub next_marker: Option<String>,
pub prefix: Option<String>,
pub timeout: Option<u64>,
}
pub const LIST_CONTAINER_OPTIONS_DEFAULT: ListContainerOptions = ListContainerOptions {
max_results: 5000,
include_metadata: false,
next_marker: None,
prefix: None,
timeout: None,
};

Просмотреть файл

@ -1,26 +1,34 @@
mod list_container_options;
pub use self::list_container_options::{ListContainerOptions, LIST_CONTAINER_OPTIONS_DEFAULT};
use chrono::{DateTime, Utc};
use futures::future::*;
use hyper::{Method, StatusCode};
use std::{fmt, str::FromStr};
use xml::Element;
pub mod requests;
use azure::core::{
enumerations,
errors::{check_status_extract_body, AzureError, TraversingError},
incompletevector::IncompleteVector,
errors::{AzureError, TraversingError},
headers::BLOB_PUBLIC_ACCESS,
lease::{LeaseDuration, LeaseState, LeaseStatus},
parsing::{cast_must, cast_optional, traverse, FromStringOptional},
util::format_header_value,
};
use azure::storage::client::Client;
const HEADER_BLOB_PUBLIC_ACCESS: &str = "x-ms-blob-public-access"; // [PublicAccess]
use chrono::{DateTime, Utc};
use http::request::Builder;
use std::collections::HashMap;
use std::{fmt, str::FromStr};
use xml::{Element, Xml};
create_enum!(PublicAccess, (None, "none"), (Container, "container"), (Blob, "blob"));
pub trait PublicAccessSupport {
type O;
fn with_public_access(self, pa: PublicAccess) -> Self::O;
}
pub trait PublicAccessRequired {
fn public_access(&self) -> PublicAccess;
fn add_header(&self, builder: &mut Builder) {
if self.public_access() != PublicAccess::None {
builder.header(BLOB_PUBLIC_ACCESS, self.public_access().as_ref());
}
}
}
#[derive(Debug, Clone)]
pub struct Container {
pub name: String,
@ -29,6 +37,16 @@ pub struct Container {
pub lease_status: LeaseStatus,
pub lease_state: LeaseState,
pub lease_duration: Option<LeaseDuration>,
pub public_access: PublicAccess,
pub has_immutability_policy: bool,
pub has_legal_hold: bool,
pub metadata: HashMap<String, String>,
}
impl AsRef<str> for Container {
fn as_ref(&self) -> &str {
&self.name
}
}
impl Container {
@ -40,10 +58,14 @@ impl Container {
lease_status: LeaseStatus::Unlocked,
lease_state: LeaseState::Available,
lease_duration: None,
public_access: PublicAccess::None,
has_immutability_policy: false,
has_legal_hold: false,
metadata: HashMap::new(),
}
}
pub fn parse(elem: &Element) -> Result<Container, AzureError> {
fn parse(elem: &Element) -> Result<Container, AzureError> {
let name = cast_must::<String>(elem, &["Name"])?;
let last_modified = cast_must::<DateTime<Utc>>(elem, &["Properties", "Last-Modified"])?;
let e_tag = cast_must::<String>(elem, &["Properties", "Etag"])?;
@ -54,6 +76,53 @@ impl Container {
let lease_status = cast_must::<LeaseStatus>(elem, &["Properties", "LeaseStatus"])?;
let public_access = match cast_optional::<PublicAccess>(elem, &["Properties", "PublicAccess"])? {
Some(pa) => pa,
None => PublicAccess::None,
};
let has_immutability_policy = cast_must::<bool>(elem, &["Properties", "HasImmutabilityPolicy"])?;
let has_legal_hold = cast_must::<bool>(elem, &["Properties", "HasLegalHold"])?;
let metadata = {
let mut hm = HashMap::new();
let metadata = traverse(elem, &["Metadata"], true)?;
for m in metadata {
for key in &m.children {
let elem = match key {
Xml::ElementNode(elem) => elem,
_ => {
return Err(AzureError::UnexpectedXMLError(String::from(
"Metadata should contain an ElementNode",
)))
}
};
let key = elem.name.to_owned();
if elem.children.is_empty() {
return Err(AzureError::UnexpectedXMLError(String::from("Metadata node should not be empty")));
}
let content = {
match elem.children[0] {
Xml::CharacterNode(ref content) => content.to_owned(),
_ => {
return Err(AzureError::UnexpectedXMLError(String::from(
"Metadata node should contain a CharacterNode with metadata value",
)))
}
}
};
hm.insert(key, content);
}
}
hm
};
Ok(Container {
name,
last_modified,
@ -61,85 +130,13 @@ impl Container {
lease_status,
lease_state,
lease_duration,
public_access,
has_immutability_policy,
has_legal_hold,
metadata,
})
}
pub fn delete(&mut self, c: &Client) -> impl Future<Item = (), Error = AzureError> {
let uri = format!("https://{}.blob.core.windows.net/{}?restype=container", c.account(), self.name);
let req = c.perform_request(&uri, Method::DELETE, |_| {}, None);
done(req)
.from_err()
.and_then(move |future_response| check_status_extract_body(future_response, StatusCode::ACCEPTED).and_then(|_| ok(())))
}
pub fn create(c: &Client, container_name: &str, pa: PublicAccess) -> impl Future<Item = (), Error = AzureError> {
let uri = format!("https://{}.blob.core.windows.net/{}?restype=container", c.account(), container_name);
let req = c.perform_request(
&uri,
Method::PUT,
|ref mut request| {
request.header(HEADER_BLOB_PUBLIC_ACCESS, format_header_value(pa).unwrap());
},
Some(&[]),
);
done(req)
.from_err()
.and_then(move |future_response| check_status_extract_body(future_response, StatusCode::CREATED).and_then(|_| ok(())))
}
// TODO
// pub fn get_acl(c : &Client, gao : &GetAclOptions)
pub fn list(c: &Client, lco: &ListContainerOptions) -> impl Future<Item = IncompleteVector<Container>, Error = AzureError> {
let mut uri = format!(
"https://{}.blob.core.windows.net?comp=list&maxresults={}",
c.account(),
lco.max_results
);
if !lco.include_metadata {
uri = format!("{}&include=metadata", uri);
}
if let Some(ref prefix) = lco.prefix {
uri = format!("{}&prefix={}", uri, prefix);
}
if let Some(ref nm) = lco.next_marker {
uri = format!("{}&marker={}", uri, nm);
}
if let Some(ref timeout) = lco.timeout {
uri = format!("{}&timeout={}", uri, timeout);
}
let req = c.perform_request(&uri, Method::GET, |_| {}, None);
done(req).from_err().and_then(move |future_response| {
check_status_extract_body(future_response, StatusCode::OK)
.and_then(|body| done(incomplete_vector_from_response(&body)).from_err())
})
}
}
fn incomplete_vector_from_response(body: &str) -> Result<IncompleteVector<Container>, AzureError> {
let elem: Element = body.parse()?;
let mut v = Vec::new();
for container in traverse(&elem, &["Containers", "Container"], true)? {
v.push(Container::parse(container)?);
}
let next_marker = match cast_optional::<String>(&elem, &["NextMarker"])? {
Some(ref nm) if nm == "" => None,
Some(nm) => Some(nm),
None => None,
};
Ok(IncompleteVector::new(next_marker, v))
}

Просмотреть файл

@ -0,0 +1,239 @@
use azure::core::errors::{check_status_extract_body, AzureError};
use azure::core::{ClientRequestIdOption, ClientRequestIdSupport, ClientRequired, TimeoutOption, TimeoutSupport};
use azure::core::{No, ToAssign, Yes};
use azure::storage::client::Client;
use azure::storage::container::{PublicAccess, PublicAccessRequired, PublicAccessSupport};
use futures::future::{done, ok, Future};
use hyper::{Method, StatusCode};
use std::collections::HashMap;
use std::marker::PhantomData;
#[derive(Debug, Clone)]
pub struct CreateBuilder<'a, ContainerNameSet, PublicAccessSet> {
p_container_name: PhantomData<ContainerNameSet>,
p_public_access: PhantomData<PublicAccessSet>,
client: &'a Client,
container_name: Option<&'a str>,
public_access: PublicAccess,
timeout: Option<u64>,
client_request_id: Option<&'a str>,
metadata: HashMap<&'a str, &'a str>,
}
impl<'a, ContainerNameSet, PublicAccessSet> ClientRequired<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet> {
fn client(&self) -> &'a Client {
self.client
}
}
//impl<'a> Default for CreateBuilder<'a, No, No, No> {
// fn default() -> Self {
// Self::new()
// }
//}
impl<'a, ContainerNameSet, PublicAccessSet> PublicAccessSupport for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where
ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign,
{
type O = CreateBuilder<'a, ContainerNameSet, Yes>;
fn with_public_access(self, public_access: PublicAccess) -> CreateBuilder<'a, ContainerNameSet, Yes> {
CreateBuilder {
p_container_name: PhantomData {},
p_public_access: PhantomData {},
client: self.client,
container_name: self.container_name,
public_access,
timeout: self.timeout,
client_request_id: self.client_request_id,
metadata: self.metadata,
}
}
}
impl<'a, ContainerNameSet> PublicAccessRequired for CreateBuilder<'a, ContainerNameSet, Yes>
where
ContainerNameSet: ToAssign,
{
fn public_access(&self) -> PublicAccess {
self.public_access
}
}
impl<'a, ContainerNameSet, PublicAccessSet> ClientRequestIdOption<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where
ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign,
{
fn client_request_id(&self) -> Option<&'a str> {
self.client_request_id
}
}
impl<'a, ContainerNameSet, PublicAccessSet> ClientRequestIdSupport<'a> for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where
ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign,
{
type O = CreateBuilder<'a, ContainerNameSet, PublicAccessSet>;
fn with_client_request_id(self, client_request_id: &'a str) -> Self::O {
CreateBuilder {
p_container_name: PhantomData {},
p_public_access: PhantomData {},
client: self.client,
container_name: self.container_name,
public_access: self.public_access,
timeout: self.timeout,
client_request_id: Some(client_request_id),
metadata: self.metadata,
}
}
}
impl<'a, ContainerNameSet, PublicAccessSet> TimeoutSupport for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where
ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign,
{
type O = CreateBuilder<'a, ContainerNameSet, PublicAccessSet>;
fn with_timeout(self, timeout: u64) -> Self::O {
CreateBuilder {
p_container_name: PhantomData {},
p_public_access: PhantomData {},
client: self.client,
container_name: self.container_name,
public_access: self.public_access,
timeout: Some(timeout),
client_request_id: self.client_request_id,
metadata: self.metadata,
}
}
}
impl<'a, ContainerNameSet, PublicAccessSet> TimeoutOption for CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where
ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign,
{
fn timeout(&self) -> Option<u64> {
self.timeout
}
}
// methods callable regardless
impl<'a, ContainerNameSet, PublicAccessSet> CreateBuilder<'a, ContainerNameSet, PublicAccessSet>
where
ContainerNameSet: ToAssign,
PublicAccessSet: ToAssign,
{
pub fn metadata(&self) -> &HashMap<&'a str, &'a str> {
&self.metadata
}
pub fn with_metadata(self, metadata: HashMap<&'a str, &'a str>) -> CreateBuilder<'a, ContainerNameSet, PublicAccessSet> {
CreateBuilder {
p_container_name: PhantomData {},
p_public_access: PhantomData {},
client: self.client,
container_name: self.container_name,
public_access: self.public_access,
timeout: self.timeout,
client_request_id: self.client_request_id,
metadata,
}
}
pub fn with_container_name(self, t: &'a str) -> CreateBuilder<'a, Yes, PublicAccessSet> {
CreateBuilder {
p_container_name: PhantomData {},
p_public_access: PhantomData {},
client: self.client,
container_name: Some(t),
public_access: self.public_access,
timeout: self.timeout,
client_request_id: self.client_request_id,
metadata: self.metadata,
}
}
}
impl<'a, PublicAccessSet> CreateBuilder<'a, Yes, PublicAccessSet>
where
PublicAccessSet: ToAssign,
{
pub fn container_name(&self) -> &'a str {
self.container_name.unwrap()
}
}
impl<'a> CreateBuilder<'a, No, No> {
pub fn new(client: &'a Client) -> CreateBuilder<'a, No, No> {
CreateBuilder {
p_container_name: PhantomData {},
p_public_access: PhantomData {},
client,
container_name: None,
public_access: PublicAccess::None,
timeout: None,
client_request_id: None,
metadata: HashMap::new(),
}
}
}
impl<'a> CreateBuilder<'a, Yes, Yes> {
pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> {
let mut uri = format!(
"https://{}.blob.core.windows.net/{}?restype=container",
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm);
}
let req = self.client().perform_request(
&uri,
Method::PUT,
|ref mut request| {
ClientRequestIdOption::add_header(&self, request);
PublicAccessRequired::add_header(&self, request);
for (key, val) in self.metadata().iter() {
request.header(&format!("x-ms-meta-{}", key) as &str, val as &str);
}
},
Some(&[]),
);
done(req)
.from_err()
.and_then(move |future_response| check_status_extract_body(future_response, StatusCode::CREATED).and_then(|_| ok(())))
}
//pub fn execute() -> impl Future<Item = (), Error = AzureError> {
// done(req).from_err().and_then(move |future_response| {
// check_status_extract_body(future_response, StatusCode::CreateBuilderd).and_then(|_| ok(()))
// })
//}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn alloc() {
let client = Client::new("a", "b").unwrap();
let create = CreateBuilder::new(&client)
.with_container_name("ciccio")
.with_public_access(PublicAccess::None);
println!("container_name == {}", create.container_name());
create.public_access();
}
}

Просмотреть файл

@ -0,0 +1,185 @@
use azure::core::errors::{check_status_extract_body, AzureError};
use azure::core::lease::LeaseId;
use azure::core::{
ClientRequestIdOption, ClientRequestIdSupport, ClientRequired, LeaseIdOption, LeaseIdSupport, TimeoutOption, TimeoutSupport,
};
use azure::core::{No, ToAssign, Yes};
use azure::storage::client::Client;
use futures::future::{done, ok, Future};
use hyper::{Method, StatusCode};
use std::marker::PhantomData;
#[derive(Debug, Clone)]
pub struct DeleteBuilder<'a, ContainerNameSet> {
p_container_name: PhantomData<ContainerNameSet>,
client: &'a Client,
container_name: Option<&'a str>,
timeout: Option<u64>,
client_request_id: Option<&'a str>,
lease_id: Option<&'a LeaseId>,
}
impl<'a, ContainerNameSet> ClientRequired<'a> for DeleteBuilder<'a, ContainerNameSet> {
fn client(&self) -> &'a Client {
self.client
}
}
impl<'a, ContainerNameSet> ClientRequestIdOption<'a> for DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
fn client_request_id(&self) -> Option<&'a str> {
self.client_request_id
}
}
impl<'a, ContainerNameSet> ClientRequestIdSupport<'a> for DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
type O = DeleteBuilder<'a, ContainerNameSet>;
fn with_client_request_id(self, client_request_id: &'a str) -> Self {
DeleteBuilder {
p_container_name: PhantomData {},
client: self.client,
container_name: self.container_name,
timeout: self.timeout,
client_request_id: Some(client_request_id),
lease_id: self.lease_id,
}
}
}
impl<'a, ContainerNameSet> LeaseIdOption<'a> for DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
fn lease_id(&self) -> Option<&'a LeaseId> {
self.lease_id
}
}
impl<'a, ContainerNameSet> LeaseIdSupport<'a> for DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
type O = DeleteBuilder<'a, ContainerNameSet>;
fn with_lease_id(self, lease_id: &'a LeaseId) -> Self::O {
DeleteBuilder {
p_container_name: PhantomData {},
client: self.client,
container_name: self.container_name,
timeout: self.timeout,
client_request_id: self.client_request_id,
lease_id: Some(lease_id),
}
}
}
impl<'a, ContainerNameSet> TimeoutOption for DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
fn timeout(&self) -> Option<u64> {
self.timeout
}
}
impl<'a, ContainerNameSet> TimeoutSupport for DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
type O = DeleteBuilder<'a, ContainerNameSet>;
fn with_timeout(self, timeout: u64) -> Self::O {
DeleteBuilder {
p_container_name: PhantomData {},
client: self.client,
container_name: self.container_name,
timeout: Some(timeout),
client_request_id: self.client_request_id,
lease_id: self.lease_id,
}
}
}
// methods callable regardless
impl<'a, ContainerNameSet> DeleteBuilder<'a, ContainerNameSet>
where
ContainerNameSet: ToAssign,
{
pub fn with_container_name(self, t: &'a str) -> DeleteBuilder<'a, Yes> {
DeleteBuilder {
p_container_name: PhantomData {},
client: self.client,
container_name: Some(t),
timeout: self.timeout,
client_request_id: self.client_request_id,
lease_id: self.lease_id,
}
}
}
impl<'a> DeleteBuilder<'a, Yes> {
pub fn container_name(&self) -> &'a str {
self.container_name.unwrap()
}
}
impl<'a> DeleteBuilder<'a, No> {
pub fn new(client: &'a Client) -> DeleteBuilder<'a, No> {
DeleteBuilder {
p_container_name: PhantomData {},
client,
container_name: None,
timeout: None,
client_request_id: None,
lease_id: None,
}
}
}
impl<'a> DeleteBuilder<'a, Yes> {
pub fn finalize(self) -> impl Future<Item = (), Error = AzureError> {
let mut uri = format!(
"https://{}.blob.core.windows.net/{}?restype=container",
self.client().account(),
self.container_name()
);
if let Some(nm) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm);
}
let req = self.client().perform_request(
&uri,
Method::DELETE,
|ref mut request| {
ClientRequestIdOption::add_header(&self, request);
LeaseIdOption::add_header(&self, request);
},
Some(&[]),
);
done(req)
.from_err()
.and_then(move |future_response| check_status_extract_body(future_response, StatusCode::ACCEPTED).and_then(|_| ok(())))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn alloc() {
let client = Client::new("a", "b").unwrap();
let del = DeleteBuilder::new(&client).with_container_name("ciccio");
println!("container_name == {}", del.container_name());
// this would fail as Client was not set
//del.finalize();
}
}

Просмотреть файл

@ -0,0 +1,231 @@
use azure::core::errors::{check_status_extract_body, AzureError};
use azure::core::incompletevector::IncompleteVector;
use azure::core::parsing::{cast_optional, traverse};
use azure::core::{
ClientRequestIdOption, ClientRequestIdSupport, ClientRequired, NextMarkerOption, NextMarkerSupport, PrefixOption, PrefixSupport,
TimeoutOption, TimeoutSupport,
};
use azure::storage::client::Client;
use azure::storage::container::Container;
use futures::future::{done, Future};
use hyper::{Method, StatusCode};
use xml::Element;
#[derive(Debug, Clone)]
pub struct ListBuilder<'a> {
client: &'a Client,
max_results: u64,
include_metadata: bool,
next_marker: Option<&'a str>,
prefix: Option<&'a str>,
timeout: Option<u64>,
client_request_id: Option<&'a str>,
}
impl<'a> ClientRequired<'a> for ListBuilder<'a> {
fn client(&self) -> &'a Client {
self.client
}
}
//impl<'a> Default for ListBuilder<'a, No> {
// fn default() -> ListBuilder<'a, No> {
// ListBuilder::new()
// }
//}
impl<'a> ListBuilder<'a> {
pub fn new(client: &'a Client) -> ListBuilder<'a> {
ListBuilder {
client,
max_results: 5000,
include_metadata: false,
next_marker: None,
prefix: None,
timeout: None,
client_request_id: None,
}
}
}
// regardless implementation
impl<'a> ListBuilder<'a> {
pub fn max_results(&self) -> u64 {
self.max_results
}
pub fn with_max_results(self, max_results: u64) -> Self {
ListBuilder {
client: self.client,
max_results,
include_metadata: self.include_metadata,
next_marker: self.next_marker,
prefix: self.prefix,
timeout: self.timeout,
client_request_id: self.client_request_id,
}
}
pub fn is_metadata_included(&self) -> bool {
self.include_metadata
}
pub fn include_metadata(self) -> Self {
ListBuilder {
client: self.client,
max_results: self.max_results,
include_metadata: true,
next_marker: self.next_marker,
prefix: self.prefix,
timeout: self.timeout,
client_request_id: self.client_request_id,
}
}
pub fn finalize(self) -> impl Future<Item = IncompleteVector<Container>, Error = AzureError> {
let mut uri = format!(
"https://{}.blob.core.windows.net?comp=list&maxresults={}",
self.client().account(),
self.max_results()
);
if self.is_metadata_included() {
uri = format!("{}&include=metadata", uri);
}
if let Some(nm) = PrefixOption::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) = TimeoutOption::to_uri_parameter(&self) {
uri = format!("{}&{}", uri, nm);
}
let req = self.client().perform_request(
&uri,
Method::GET,
|ref mut request| {
ClientRequestIdOption::add_header(&self, request);
},
None,
);
done(req).from_err().and_then(move |future_response| {
check_status_extract_body(future_response, StatusCode::OK)
.and_then(|body| done(incomplete_vector_from_response(&body)).from_err())
})
}
}
impl<'a> PrefixOption<'a> for ListBuilder<'a> {
fn prefix(&self) -> Option<&'a str> {
self.prefix
}
}
impl<'a> PrefixSupport<'a> for ListBuilder<'a> {
type O = ListBuilder<'a>;
fn with_prefix(self, prefix: &'a str) -> Self::O {
ListBuilder {
client: self.client,
max_results: self.max_results,
include_metadata: self.include_metadata,
next_marker: self.next_marker,
prefix: Some(prefix),
timeout: self.timeout,
client_request_id: self.client_request_id,
}
}
}
impl<'a> TimeoutOption for ListBuilder<'a> {
fn timeout(&self) -> Option<u64> {
self.timeout
}
}
impl<'a> TimeoutSupport for ListBuilder<'a> {
type O = ListBuilder<'a>;
fn with_timeout(self, timeout: u64) -> Self::O {
ListBuilder {
client: self.client,
max_results: self.max_results,
include_metadata: self.include_metadata,
next_marker: self.next_marker,
prefix: self.prefix,
timeout: Some(timeout),
client_request_id: self.client_request_id,
}
}
}
impl<'a> NextMarkerOption<'a> for ListBuilder<'a> {
fn next_marker(&self) -> Option<&'a str> {
match self.next_marker {
Some(nm) => Some(nm),
None => None,
}
}
}
impl<'a> NextMarkerSupport<'a> for ListBuilder<'a> {
type O = ListBuilder<'a>;
fn with_next_marker(self, next_marker: &'a str) -> Self::O {
ListBuilder {
client: self.client,
max_results: self.max_results,
include_metadata: self.include_metadata,
next_marker: Some(next_marker),
prefix: self.prefix,
timeout: self.timeout,
client_request_id: self.client_request_id,
}
}
}
impl<'a> ClientRequestIdOption<'a> for ListBuilder<'a> {
fn client_request_id(&self) -> Option<&'a str> {
self.client_request_id
}
}
impl<'a> ClientRequestIdSupport<'a> for ListBuilder<'a> {
type O = ListBuilder<'a>;
fn with_client_request_id(self, client_request_id: &'a str) -> Self::O {
ListBuilder {
client: self.client,
max_results: self.max_results,
include_metadata: self.include_metadata,
next_marker: self.next_marker,
prefix: self.prefix,
timeout: self.timeout,
client_request_id: Some(client_request_id),
}
}
}
fn incomplete_vector_from_response(body: &str) -> Result<IncompleteVector<Container>, AzureError> {
let elem: Element = body.parse()?;
let mut v = Vec::new();
for container in traverse(&elem, &["Containers", "Container"], true)? {
v.push(Container::parse(container)?);
}
let next_marker = match cast_optional::<String>(&elem, &["NextMarker"])? {
Some(ref nm) if nm == "" => None,
Some(nm) => Some(nm),
None => None,
};
Ok(IncompleteVector::new(next_marker, v))
}

Просмотреть файл

@ -0,0 +1,6 @@
mod create_builder;
mod delete_builder;
mod list_builder;
pub use self::create_builder::CreateBuilder;
pub use self::delete_builder::DeleteBuilder;
pub use self::list_builder::ListBuilder;

Просмотреть файл

@ -22,7 +22,6 @@ pub const HEADER_VERSION: &str = "x-ms-version"; //=> [String] }
pub const HEADER_DATE: &str = "x-ms-date"; //=> [String] }
pub const HEADER_CONTENT_MD5: &str = "Content-MD5"; //=> [String] }
pub const HEADER_RANGE: &str = "x-ms-range"; // => [range::Range] }
pub const HEADER_LEASE_ID: &str = "x-ms-lease-id"; //=> [LeaseId] }
pub const HEADER_LEASE_STATUS: &str = "x-ms-lease-status"; //=> [LeaseStatus] }
pub const HEADER_LEASE_STATE: &str = "x-ms-lease-state"; //=> [LeaseState] }
pub const HEADER_LEASE_ACTION: &str = "x-ms-lease-action"; //=> [LeaseAction] }
@ -30,7 +29,6 @@ pub const HEADER_LEASE_DURATION: &str = "x-ms-lease-duration"; //=> [LeaseDurati
pub const HEADER_LEASE_BREAK_PERIOD: &str = "x-ms-lease-break-period"; //=> [u32] }
pub const HEADER_PROPOSED_LEASE_ID: &str = "x-ms-proposed-lease-id"; //=> [LeaseId] }
pub const HEADER_RANGE_GET_CONTENT_MD5: &str = "x-ms-range-get-content-md5"; //=> [bool] }
pub const HEADER_CLIENT_REQUEST_ID: &str = "x-ms-client-request-id"; //=> [String] }
pub const HEADER_REQUEST_ID: &str = "x-ms-request-id"; //=> [String] }
fn generate_authorization(h: &HeaderMap, u: &url::Url, method: Method, hmac_key: &str, service_type: ServiceType) -> String {

Просмотреть файл

@ -1,69 +1,76 @@
#![cfg(all(test, feature = "test_e2e"))]
extern crate azure_sdk_for_rust;
extern crate chrono;
extern crate env_logger;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate tokio_core;
extern crate chrono;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate serde;
extern crate uuid;
use uuid::Uuid;
use std::ops::Deref;
use tokio_core::reactor::Core;
use azure_sdk_for_rust::core::{
errors::AzureError,
lease::{LeaseState, LeaseStatus},
};
use azure_sdk_for_rust::core::{NextMarkerSupport, PrefixSupport};
use azure_sdk_for_rust::storage::{
blob::{get_block_list, put_block_list, Blob, BlobType, BlockListType, PUT_BLOCK_OPTIONS_DEFAULT, PUT_OPTIONS_DEFAULT},
client::Client,
container::{Container, PublicAccess, LIST_CONTAINER_OPTIONS_DEFAULT},
container::{Container, PublicAccess, PublicAccessSupport},
};
use chrono::Utc;
use futures::Future;
use hyper::mime::Mime;
use std::ops::Deref;
use tokio_core::reactor::Core;
use uuid::Uuid;
#[test]
fn create_and_delete_container() {
use azure_sdk_for_rust::storage::client::Container;
let name: &'static str = "azuresdkrustetoets";
let (client, mut core) = initialize().unwrap();
core.run(Container::create(&client, name, PublicAccess::Container)).unwrap();
core.run(
client
.create()
.with_container_name(name)
.with_public_access(PublicAccess::Container)
.finalize(),
).unwrap();
let mut lco = LIST_CONTAINER_OPTIONS_DEFAULT.clone();
lco.prefix = Some(name.to_owned());
let list = core.run(Container::list(&client, &lco)).unwrap();
let cont_list: Vec<&Container> = list.deref().into_iter().filter(|e| e.name == name).collect();
let list = core.run(client.list().with_prefix(name).finalize()).unwrap();
let cont_list: Vec<&azure_sdk_for_rust::storage::container::Container> = list.deref().into_iter().filter(|e| e.name == name).collect();
if cont_list.len() != 1 {
panic!("More than 1 container returned with the same name!");
}
let mut cont = cont_list[0].clone();
let cont_delete = client.delete().with_container_name(&cont_list[0].name).finalize();
core.run(cont.delete(&client)).unwrap();
core.run(cont_delete).unwrap();
}
#[test]
fn put_and_get_block_list() {
use azure_sdk_for_rust::storage::client::Container as ContainerTrait;
let u = Uuid::new_v4();
let mut container = Container::new(&format!("sdkrust{}", u));
let container = Container::new(&format!("sdkrust{}", u));
let name = "asdkrustputblock.txt";
let (client, mut core) = initialize().unwrap();
core.run(Container::create(&client, &container.name, PublicAccess::Container))
.expect("container already present");
core.run(
client
.create()
.with_container_name(&container.name)
.with_public_access(PublicAccess::Container)
.finalize(),
).expect("container already present");
let contents1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
let contents2 = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
@ -76,7 +83,7 @@ fn put_and_get_block_list() {
last_modified: chrono::Utc::now(),
etag: "".to_owned(),
content_length: 0,
content_type: Some("text/plain".parse::<Mime>().unwrap()),
content_type: Some("text/plain".to_owned()),
content_encoding: None,
content_language: None,
content_md5: None,
@ -126,24 +133,38 @@ fn put_and_get_block_list() {
let future = Blob::delete(&client, &container.name, &name, None).map(|_| println!("Blob deleted!"));
core.run(future).unwrap();
core.run(container.delete(&client).map(|_| println!("container {} deleted!", container.name)))
.unwrap();
core.run(
client
.delete()
.with_container_name(container.as_ref())
.finalize()
.map(|_| println!("container {} deleted!", container.name)),
).unwrap();
}
#[test]
fn list_containers() {
use azure_sdk_for_rust::storage::client::Container as ContainerTrait;
let (client, mut core) = initialize().unwrap();
trace!("running list_containers");
let mut lco = LIST_CONTAINER_OPTIONS_DEFAULT.clone();
lco.max_results = 2;
let mut next_marker: Option<String> = None;
loop {
let ret = core.run(Container::list(&client, &lco)).unwrap();
let ret = {
let builder = client.list().with_max_results(2);
if let Some(nm) = next_marker {
core.run(builder.with_next_marker(&nm).finalize()).unwrap()
} else {
core.run(builder.finalize()).unwrap()
}
};
trace!("ret {:?}\n\n", ret);
if !ret.is_complete() {
lco.next_marker = Some(ret.token().unwrap().to_owned());
next_marker = Some(ret.token().unwrap().to_owned());
} else {
break;
}
@ -152,6 +173,8 @@ fn list_containers() {
#[test]
fn put_blob() {
use azure_sdk_for_rust::storage::client::Container as ContainerTrait;
let (client, mut core) = initialize().unwrap();
let blob_name: &'static str = "m1";
@ -159,13 +182,19 @@ fn put_blob() {
let value = "abcdef";
if core
.run(Container::list(&client, &LIST_CONTAINER_OPTIONS_DEFAULT))
.run(client.list().finalize())
.unwrap()
.iter()
.find(|x| x.name == container_name)
.is_none()
{
core.run(Container::create(&client, container_name, PublicAccess::Blob)).unwrap();
core.run(
client
.create()
.with_container_name(container_name)
.with_public_access(PublicAccess::Blob)
.finalize(),
).unwrap();
}
let new_blob = Blob {
@ -175,7 +204,7 @@ fn put_blob() {
last_modified: Utc::now(),
etag: "".to_owned(),
content_length: value.as_bytes().len() as u64,
content_type: Some("application/octet-stream".parse::<Mime>().unwrap()),
content_type: Some("application/octet-stream".to_owned()),
content_encoding: None,
content_language: None,
content_md5: None,
@ -204,5 +233,5 @@ fn initialize() -> Result<(Client, Core), AzureError> {
let master_key = std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!");
let core = Core::new()?;
Ok((Client::new(&core.handle(), &account, &master_key)?, core))
Ok((Client::new(&account, &master_key)?, core))
}

Просмотреть файл

@ -45,8 +45,5 @@ fn create_client() -> Result<(Client, Core), AzureError> {
let core = Core::new()?;
Ok((
Client::new(core.handle(), &service_bus_namespace, &event_hub_name, &policy_name, &policy_key),
core,
))
Ok((Client::new(service_bus_namespace, event_hub_name, policy_name, policy_key)?, core))
}

Просмотреть файл

@ -10,10 +10,10 @@ use azure_sdk_for_rust::core::lease::{LeaseState, LeaseStatus};
use azure_sdk_for_rust::core::range::Range;
use azure_sdk_for_rust::storage::blob::{Blob, BlobType, PUT_OPTIONS_DEFAULT};
use azure_sdk_for_rust::storage::client::Client;
use azure_sdk_for_rust::storage::container::{Container, PublicAccess, LIST_CONTAINER_OPTIONS_DEFAULT};
use azure_sdk_for_rust::storage::client::Container as ContainerTrait;
use azure_sdk_for_rust::storage::container::{PublicAccess, PublicAccessSupport};
use futures::future::ok;
use futures::prelude::*;
use hyper::mime::Mime;
use tokio_core::reactor::Core;
#[test]
@ -30,15 +30,21 @@ fn code() -> Result<(), Box<std::error::Error>> {
let master_key = std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!");
let mut reactor = Core::new()?;
let client = Client::new(&reactor.handle(), &account, &master_key)?;
let client = Client::new(&account, &master_key)?;
if reactor
.run(Container::list(&client, &LIST_CONTAINER_OPTIONS_DEFAULT))?
.run(client.list().finalize())?
.iter()
.find(|x| x.name == container_name)
.is_none()
{
reactor.run(Container::create(&client, container_name, PublicAccess::Blob))?;
reactor.run(
client
.create()
.with_container_name(container_name)
.with_public_access(PublicAccess::Blob)
.finalize(),
)?;
}
let string = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
@ -50,7 +56,7 @@ fn code() -> Result<(), Box<std::error::Error>> {
last_modified: chrono::Utc::now(),
etag: "".to_owned(),
content_length: string.len() as u64,
content_type: Some("text/plain".parse::<Mime>().unwrap()),
content_type: Some("text/plain".to_owned()),
content_encoding: None,
content_language: None,
content_md5: None,