Skip to content

More headers cleanup #864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/core/src/error/http_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl std::error::Error for HttpError {}
///
/// For more info, see [here](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#handling-errors)
fn get_error_code_from_header(response: &Response) -> Option<String> {
response.headers().get_as_string(&headers::ERROR_CODE)
response.headers().get_optional_string(&headers::ERROR_CODE)
}

/// Gets the error code if it's present in the body
Expand Down
82 changes: 46 additions & 36 deletions sdk/core/src/headers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,68 +59,78 @@ impl Headers {
Self(Default::default())
}

/// Get a header value given a specific header name
pub fn get(&self, key: &HeaderName) -> Option<&HeaderValue> {
self.0.get(key)
}

/// Get a header value or error if it is not found
pub fn get_or_err(&self, key: &HeaderName) -> crate::Result<&HeaderValue> {
self.get(key).ok_or_else(|| {
Error::with_message(ErrorKind::DataConversion, || {
format!("header not found {}", key.as_str())
})
})
/// Get a header value as a String or error if it is not found
pub fn get_string(&self, key: &HeaderName) -> crate::Result<String> {
Ok(self.get_str(key)?.to_owned())
}

/// Get a header value as a str
pub fn get_as_str(&self, key: &HeaderName) -> Option<&str> {
self.get(key).map(|v| v.as_str())
/// Optionally get a header value as a String
pub fn get_optional_string(&self, key: &HeaderName) -> Option<String> {
self.get_string(key).ok()
}

/// Get a header value as a str or error if it is not found
pub fn get_as_str_or_err(&self, key: &HeaderName) -> crate::Result<&str> {
self.get_or_err(key).map(|v| v.as_str())
pub fn get_str(&self, key: &HeaderName) -> crate::Result<&str> {
self.get_with(key, |s| crate::Result::Ok(s.as_str()))
}

/// Get a header value as a String
pub fn get_as_string(&self, key: &HeaderName) -> Option<String> {
self.get(key).map(|v| v.as_str().to_string())
/// Optionally get a header value as a str
pub fn get_optional_str(&self, key: &HeaderName) -> Option<&str> {
self.get_str(key).ok()
}

/// Get a header value as a String or error if it is not found
pub fn get_as_string_or_err(&self, key: &HeaderName) -> crate::Result<String> {
self.get_or_err(key).map(|v| v.as_str().to_string())
/// Get a header value parsing it as the type or error if it's not found or it fails to parse
pub fn get_as<V, E>(&self, key: &HeaderName) -> crate::Result<V>
where
V: FromStr<Err = E>,
E: std::error::Error + Send + Sync + 'static,
{
self.get_with(key, |s| s.as_str().parse())
}

/// Optionally get a header value parsing it as the type or error if it fails to parse
pub fn get_optional_as<V, E>(&self, key: &HeaderName) -> crate::Result<Option<V>>
where
V: FromStr<Err = E>,
E: std::error::Error + Send + Sync + 'static,
{
self.get(key)
.map(|v: &HeaderValue| {
let v = v.as_str();
v.parse::<V>().with_context(ErrorKind::DataConversion, || {
let ty = std::any::type_name::<V>();
format!("unable to parse header '{key:?}: {v}' into {ty}",)
})
})
.transpose()
self.get_optional_with(key, |s| s.as_str().parse())
}

pub fn get_as<V, E>(&self, key: &HeaderName) -> crate::Result<V>
/// Get a header value using the parser or error if it is not found or fails to parse
pub fn get_with<'a, V, F, E>(&'a self, key: &HeaderName, parser: F) -> crate::Result<V>
where
V: FromStr<Err = E>,
F: FnOnce(&'a HeaderValue) -> Result<V, E>,
E: std::error::Error + Send + Sync + 'static,
{
self.get_optional_as(key)?.ok_or_else(|| {
Error::with_message(ErrorKind::Other, || {
self.get_optional_with(key, parser)?.ok_or_else(|| {
Error::with_message(ErrorKind::DataConversion, || {
format!("header not found {}", key.as_str())
})
})
}

/// Optionally get a header value using the parser or error if it fails to parse
pub fn get_optional_with<'a, V, F, E>(
&'a self,
key: &HeaderName,
parser: F,
) -> crate::Result<Option<V>>
where
F: FnOnce(&'a HeaderValue) -> Result<V, E>,
E: std::error::Error + Send + Sync + 'static,
{
self.0
.get(key)
.map(|v: &HeaderValue| {
parser(v).with_context(ErrorKind::DataConversion, || {
let ty = std::any::type_name::<V>();
format!("unable to parse header '{key:?}: {v:?}' into {ty}",)
})
})
.transpose()
}

/// Insert a header name/value pair
pub fn insert<K, V>(&mut self, key: K, value: V)
where
Expand Down
22 changes: 11 additions & 11 deletions sdk/core/src/headers/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn request_id_from_headers(headers: &Headers) -> crate::Result<RequestId> {
}

pub fn client_request_id_from_headers_optional(headers: &Headers) -> Option<String> {
headers.get_as_string(&CLIENT_REQUEST_ID)
headers.get_optional_string(&CLIENT_REQUEST_ID)
}

pub fn last_modified_from_headers_optional(
Expand All @@ -57,7 +57,7 @@ pub fn rfc2822_from_headers_mandatory(
headers: &Headers,
header_name: &HeaderName,
) -> crate::Result<DateTime<Utc>> {
let date = headers.get_as_str_or_err(header_name)?;
let date = headers.get_str(header_name)?;
utc_date_from_rfc2822(date)
}

Expand All @@ -69,23 +69,23 @@ pub fn utc_date_from_rfc2822(date: &str) -> crate::Result<DateTime<Utc>> {
pub fn continuation_token_from_headers_optional(
headers: &Headers,
) -> crate::Result<Option<String>> {
Ok(headers.get_as_string(&CONTINUATION))
Ok(headers.get_optional_string(&CONTINUATION))
}

pub fn sku_name_from_headers(headers: &Headers) -> crate::Result<String> {
headers.get_as_string_or_err(&SKU_NAME)
headers.get_string(&SKU_NAME)
}

pub fn account_kind_from_headers(headers: &Headers) -> crate::Result<String> {
headers.get_as_string_or_err(&ACCOUNT_KIND)
headers.get_string(&ACCOUNT_KIND)
}

pub fn etag_from_headers_optional(headers: &Headers) -> crate::Result<Option<String>> {
Ok(headers.get_as_string(&ETAG))
Ok(headers.get_optional_string(&ETAG))
}

pub fn etag_from_headers(headers: &Headers) -> crate::Result<String> {
headers.get_as_string_or_err(&ETAG)
headers.get_string(&ETAG)
}

pub fn lease_time_from_headers(headers: &Headers) -> crate::Result<u8> {
Expand All @@ -107,23 +107,23 @@ pub fn sequence_number_from_headers(headers: &Headers) -> crate::Result<u64> {
}

pub fn session_token_from_headers(headers: &Headers) -> crate::Result<SessionToken> {
headers.get_as_string_or_err(&SESSION_TOKEN)
headers.get_string(&SESSION_TOKEN)
}

pub fn server_from_headers(headers: &Headers) -> crate::Result<&str> {
headers.get_as_str_or_err(&SERVER)
headers.get_str(&SERVER)
}

pub fn version_from_headers(headers: &Headers) -> crate::Result<&str> {
headers.get_as_str_or_err(&VERSION)
headers.get_str(&VERSION)
}

pub fn request_server_encrypted_from_headers(headers: &Headers) -> crate::Result<bool> {
headers.get_as(&REQUEST_SERVER_ENCRYPTED)
}

pub fn content_type_from_headers(headers: &Headers) -> crate::Result<&str> {
headers.get_as_str_or_err(&CONTENT_TYPE)
headers.get_str(&CONTENT_TYPE)
}

pub fn item_count_from_headers(headers: &Headers) -> crate::Result<u32> {
Expand Down
9 changes: 8 additions & 1 deletion sdk/core/src/models/etag.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use std::fmt;
use std::{fmt, str::FromStr};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Etag(String);
Expand All @@ -19,6 +19,13 @@ impl AsRef<str> for Etag {
}
}

impl FromStr for Etag {
type Err = crate::error::Error;
fn from_str(s: &str) -> crate::Result<Self> {
Ok(Self(s.into()))
}
}

impl fmt::Display for Etag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/src/request_options/next_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl NextMarker {
}

pub fn from_header_optional(headers: &Headers) -> crate::Result<Option<Self>> {
let header_as_str = headers.get_as_str(&headers::CONTINUATION);
let header_as_str = headers.get_optional_str(&headers::CONTINUATION);

Ok(header_as_str
.filter(|h| !h.is_empty())
Expand Down
22 changes: 11 additions & 11 deletions sdk/data_cosmos/src/headers/from_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,24 @@ pub(crate) fn activity_id_from_headers(headers: &Headers) -> azure_core::Result<
}

pub(crate) fn content_path_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&HEADER_CONTENT_PATH)
headers.get_str(&HEADER_CONTENT_PATH)
}

pub(crate) fn alt_content_path_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&HEADER_ALT_CONTENT_PATH)
headers.get_str(&HEADER_ALT_CONTENT_PATH)
}

pub(crate) fn resource_quota_from_headers(
headers: &Headers,
) -> azure_core::Result<Vec<ResourceQuota>> {
let s = headers.get_as_str_or_err(&HEADER_RESOURCE_QUOTA)?;
let s = headers.get_str(&HEADER_RESOURCE_QUOTA)?;
resource_quotas_from_str(s)
}

pub(crate) fn resource_usage_from_headers(
headers: &Headers,
) -> azure_core::Result<Vec<ResourceQuota>> {
let s = headers.get_as_str_or_err(&HEADER_RESOURCE_USAGE)?;
let s = headers.get_str(&HEADER_RESOURCE_USAGE)?;
resource_quotas_from_str(s)
}

Expand Down Expand Up @@ -104,7 +104,7 @@ pub(crate) fn transport_request_id_from_headers(headers: &Headers) -> azure_core
}

pub(crate) fn global_committed_lsn_from_headers(headers: &Headers) -> azure_core::Result<u64> {
let s = headers.get_as_str_or_err(&HEADER_GLOBAL_COMMITTED_LSN)?;
let s = headers.get_str(&HEADER_GLOBAL_COMMITTED_LSN)?;
Ok(if s == "-1" {
0
} else {
Expand Down Expand Up @@ -140,23 +140,23 @@ pub(crate) fn current_replica_set_size_from_headers_optional(
}

pub(crate) fn schema_version_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&HEADER_SCHEMA_VERSION)
headers.get_str(&HEADER_SCHEMA_VERSION)
}

pub(crate) fn server_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&headers::SERVER)
headers.get_str(&headers::SERVER)
}

pub(crate) fn service_version_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&HEADER_SERVICE_VERSION)
headers.get_str(&HEADER_SERVICE_VERSION)
}

pub(crate) fn content_location_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&headers::CONTENT_LOCATION)
headers.get_str(&headers::CONTENT_LOCATION)
}

pub(crate) fn gateway_version_from_headers(headers: &Headers) -> azure_core::Result<&str> {
headers.get_as_str_or_err(&HEADER_GATEWAY_VERSION)
headers.get_str(&HEADER_GATEWAY_VERSION)
}

pub(crate) fn max_media_storage_usage_mb_from_headers(
Expand All @@ -173,7 +173,7 @@ fn _date_from_headers(
headers: &Headers,
header_name: &HeaderName,
) -> azure_core::Result<DateTime<Utc>> {
let date = headers.get_as_str_or_err(header_name)?;
let date = headers.get_str(header_name)?;
// since Azure returns "GMT" instead of +0000 as timezone we replace it ourselves.
// For example: Wed, 15 Jan 2020 23:39:44.369 GMT
let date = date.replace("GMT", "+0000");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ impl ContinuationNextPartitionAndRowKey {
}

pub fn from_header_optional(headers: &Headers) -> azure_core::Result<Option<Self>> {
let partition_header_as_str = headers.get_as_str(&HeaderName::from_static(
let partition_header_as_str = headers.get_optional_str(&HeaderName::from_static(
"x-ms-continuation-NextPartitionKey",
));

let row_header_as_str =
headers.get_as_str(&HeaderName::from_static("x-ms-continuation-NextRowKey"));
headers.get_optional_str(&HeaderName::from_static("x-ms-continuation-NextRowKey"));

Ok(partition_header_as_str.filter(|h| !h.is_empty()).map(|h| {
ContinuationNextPartitionAndRowKey::new(
Expand Down
2 changes: 1 addition & 1 deletion sdk/data_tables/src/continuation_next_table_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl ContinuationNextTableName {

pub fn from_header_optional(headers: &Headers) -> azure_core::Result<Option<Self>> {
let header_as_str =
headers.get_as_str(&HeaderName::from_static("x-ms-continuation-NextTableName"));
headers.get_optional_str(&HeaderName::from_static("x-ms-continuation-NextTableName"));

Ok(header_as_str
.filter(|h| !h.is_empty())
Expand Down
2 changes: 1 addition & 1 deletion sdk/data_tables/src/responses/insert_entity_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ where
fn try_from(response: CollectedResponse) -> azure_core::Result<Self> {
let headers = response.headers();
let entity_with_metadata =
match headers.get_as_str_or_err(&HeaderName::from_static("preference-applied"))? {
match headers.get_str(&HeaderName::from_static("preference-applied"))? {
"return-no-content" => None,
"return-content" => Some(response.clone().try_into()?),
_ => {
Expand Down
2 changes: 1 addition & 1 deletion sdk/iot_hub/src/service/responses/query_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl std::convert::TryFrom<crate::service::CollectedResponse> for QueryResponse
Ok(QueryResponse {
result: serde_json::from_slice(body)?,
continuation_token: continuation_token_from_headers_optional(headers)?,
item_type: headers.get_as_string_or_err(&headers::ITEM_TYPE)?,
item_type: headers.get_string(&headers::ITEM_TYPE)?,
})
}
}
2 changes: 1 addition & 1 deletion sdk/messaging_servicebus/src/service_bus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ async fn peek_lock_message2(
let status = res.status();
let lock_location = res
.headers()
.get_as_string(&headers::LOCATION)
.get_optional_string(&headers::LOCATION)
.unwrap_or_default();
let body = body_bytes_to_utf8(&res.into_body().await)?;

Expand Down
1 change: 1 addition & 0 deletions sdk/storage/src/account/operations/find_blobs_by_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl FindBlobsByTagsBuilder {
next_marker: NextMarker => Some(next_marker),
max_results: MaxResults => Some(max_results),
timeout: Timeout => Some(timeout),
context: Context => context,
}

// TODO: Make this a stream instead of a `Future`
Expand Down
6 changes: 3 additions & 3 deletions sdk/storage/src/authorization_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn generate_authorization(
}

fn add_if_exists<'a>(h: &'a Headers, key: &HeaderName) -> &'a str {
h.get_as_str(key).unwrap_or_default()
h.get_optional_str(key).unwrap_or_default()
}

#[allow(unknown_lints)]
Expand All @@ -117,7 +117,7 @@ fn string_to_sign(
// content length must only be specified if != 0
// this is valid from 2015-02-21
let content_length = h
.get_as_str(&CONTENT_LENGTH)
.get_optional_str(&CONTENT_LENGTH)
.filter(|&v| v != "0")
.unwrap_or_default();
format!(
Expand Down Expand Up @@ -151,7 +151,7 @@ fn canonicalize_header(headers: &Headers) -> String {
let mut result = String::new();

for header_name in names {
let value = headers.get_as_str(header_name).unwrap();
let value = headers.get_optional_str(header_name).unwrap();
let name = header_name.as_str();
result = format!("{result}{name}:{value}\n");
}
Expand Down
Loading