builder pattern for container (uncompleted)
This commit is contained in:
Родитель
afcfc120ba
Коммит
bdb33a8727
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче