Skip to content

Commit 0d28f09

Browse files
authored
More headers cleanup (#864)
* Consistent header method naming * Parse more headers
1 parent bbd3c79 commit 0d28f09

File tree

24 files changed

+141
-147
lines changed

24 files changed

+141
-147
lines changed

sdk/core/src/error/http_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl std::error::Error for HttpError {}
7171
///
7272
/// For more info, see [here](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#handling-errors)
7373
fn get_error_code_from_header(response: &Response) -> Option<String> {
74-
response.headers().get_as_string(&headers::ERROR_CODE)
74+
response.headers().get_optional_string(&headers::ERROR_CODE)
7575
}
7676

7777
/// Gets the error code if it's present in the body

sdk/core/src/headers/mod.rs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -59,68 +59,78 @@ impl Headers {
5959
Self(Default::default())
6060
}
6161

62-
/// Get a header value given a specific header name
63-
pub fn get(&self, key: &HeaderName) -> Option<&HeaderValue> {
64-
self.0.get(key)
65-
}
66-
67-
/// Get a header value or error if it is not found
68-
pub fn get_or_err(&self, key: &HeaderName) -> crate::Result<&HeaderValue> {
69-
self.get(key).ok_or_else(|| {
70-
Error::with_message(ErrorKind::DataConversion, || {
71-
format!("header not found {}", key.as_str())
72-
})
73-
})
62+
/// Get a header value as a String or error if it is not found
63+
pub fn get_string(&self, key: &HeaderName) -> crate::Result<String> {
64+
Ok(self.get_str(key)?.to_owned())
7465
}
7566

76-
/// Get a header value as a str
77-
pub fn get_as_str(&self, key: &HeaderName) -> Option<&str> {
78-
self.get(key).map(|v| v.as_str())
67+
/// Optionally get a header value as a String
68+
pub fn get_optional_string(&self, key: &HeaderName) -> Option<String> {
69+
self.get_string(key).ok()
7970
}
8071

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

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

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

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

112-
pub fn get_as<V, E>(&self, key: &HeaderName) -> crate::Result<V>
100+
/// Get a header value using the parser or error if it is not found or fails to parse
101+
pub fn get_with<'a, V, F, E>(&'a self, key: &HeaderName, parser: F) -> crate::Result<V>
113102
where
114-
V: FromStr<Err = E>,
103+
F: FnOnce(&'a HeaderValue) -> Result<V, E>,
115104
E: std::error::Error + Send + Sync + 'static,
116105
{
117-
self.get_optional_as(key)?.ok_or_else(|| {
118-
Error::with_message(ErrorKind::Other, || {
106+
self.get_optional_with(key, parser)?.ok_or_else(|| {
107+
Error::with_message(ErrorKind::DataConversion, || {
119108
format!("header not found {}", key.as_str())
120109
})
121110
})
122111
}
123112

113+
/// Optionally get a header value using the parser or error if it fails to parse
114+
pub fn get_optional_with<'a, V, F, E>(
115+
&'a self,
116+
key: &HeaderName,
117+
parser: F,
118+
) -> crate::Result<Option<V>>
119+
where
120+
F: FnOnce(&'a HeaderValue) -> Result<V, E>,
121+
E: std::error::Error + Send + Sync + 'static,
122+
{
123+
self.0
124+
.get(key)
125+
.map(|v: &HeaderValue| {
126+
parser(v).with_context(ErrorKind::DataConversion, || {
127+
let ty = std::any::type_name::<V>();
128+
format!("unable to parse header '{key:?}: {v:?}' into {ty}",)
129+
})
130+
})
131+
.transpose()
132+
}
133+
124134
/// Insert a header name/value pair
125135
pub fn insert<K, V>(&mut self, key: K, value: V)
126136
where

sdk/core/src/headers/utilities.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub fn request_id_from_headers(headers: &Headers) -> crate::Result<RequestId> {
3636
}
3737

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

4242
pub fn last_modified_from_headers_optional(
@@ -57,7 +57,7 @@ pub fn rfc2822_from_headers_mandatory(
5757
headers: &Headers,
5858
header_name: &HeaderName,
5959
) -> crate::Result<DateTime<Utc>> {
60-
let date = headers.get_as_str_or_err(header_name)?;
60+
let date = headers.get_str(header_name)?;
6161
utc_date_from_rfc2822(date)
6262
}
6363

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

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

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

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

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

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

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

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

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

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

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

129129
pub fn item_count_from_headers(headers: &Headers) -> crate::Result<u32> {

sdk/core/src/models/etag.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use serde::{Deserialize, Serialize};
2-
use std::fmt;
2+
use std::{fmt, str::FromStr};
33

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

22+
impl FromStr for Etag {
23+
type Err = crate::error::Error;
24+
fn from_str(s: &str) -> crate::Result<Self> {
25+
Ok(Self(s.into()))
26+
}
27+
}
28+
2229
impl fmt::Display for Etag {
2330
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2431
write!(f, "{}", self.0)

sdk/core/src/request_options/next_marker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl NextMarker {
3232
}
3333

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

3737
Ok(header_as_str
3838
.filter(|h| !h.is_empty())

sdk/data_cosmos/src/headers/from_headers.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@ pub(crate) fn activity_id_from_headers(headers: &Headers) -> azure_core::Result<
2424
}
2525

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

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

3434
pub(crate) fn resource_quota_from_headers(
3535
headers: &Headers,
3636
) -> azure_core::Result<Vec<ResourceQuota>> {
37-
let s = headers.get_as_str_or_err(&HEADER_RESOURCE_QUOTA)?;
37+
let s = headers.get_str(&HEADER_RESOURCE_QUOTA)?;
3838
resource_quotas_from_str(s)
3939
}
4040

4141
pub(crate) fn resource_usage_from_headers(
4242
headers: &Headers,
4343
) -> azure_core::Result<Vec<ResourceQuota>> {
44-
let s = headers.get_as_str_or_err(&HEADER_RESOURCE_USAGE)?;
44+
let s = headers.get_str(&HEADER_RESOURCE_USAGE)?;
4545
resource_quotas_from_str(s)
4646
}
4747

@@ -104,7 +104,7 @@ pub(crate) fn transport_request_id_from_headers(headers: &Headers) -> azure_core
104104
}
105105

106106
pub(crate) fn global_committed_lsn_from_headers(headers: &Headers) -> azure_core::Result<u64> {
107-
let s = headers.get_as_str_or_err(&HEADER_GLOBAL_COMMITTED_LSN)?;
107+
let s = headers.get_str(&HEADER_GLOBAL_COMMITTED_LSN)?;
108108
Ok(if s == "-1" {
109109
0
110110
} else {
@@ -140,23 +140,23 @@ pub(crate) fn current_replica_set_size_from_headers_optional(
140140
}
141141

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

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

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

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

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

162162
pub(crate) fn max_media_storage_usage_mb_from_headers(
@@ -173,7 +173,7 @@ fn _date_from_headers(
173173
headers: &Headers,
174174
header_name: &HeaderName,
175175
) -> azure_core::Result<DateTime<Utc>> {
176-
let date = headers.get_as_str_or_err(header_name)?;
176+
let date = headers.get_str(header_name)?;
177177
// since Azure returns "GMT" instead of +0000 as timezone we replace it ourselves.
178178
// For example: Wed, 15 Jan 2020 23:39:44.369 GMT
179179
let date = date.replace("GMT", "+0000");

sdk/data_tables/src/continuation_next_partition_and_row_key.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ impl ContinuationNextPartitionAndRowKey {
1919
}
2020

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

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

2929
Ok(partition_header_as_str.filter(|h| !h.is_empty()).map(|h| {
3030
ContinuationNextPartitionAndRowKey::new(

sdk/data_tables/src/continuation_next_table_name.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl ContinuationNextTableName {
1717

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

2222
Ok(header_as_str
2323
.filter(|h| !h.is_empty())

sdk/data_tables/src/responses/insert_entity_response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ where
2929
fn try_from(response: CollectedResponse) -> azure_core::Result<Self> {
3030
let headers = response.headers();
3131
let entity_with_metadata =
32-
match headers.get_as_str_or_err(&HeaderName::from_static("preference-applied"))? {
32+
match headers.get_str(&HeaderName::from_static("preference-applied"))? {
3333
"return-no-content" => None,
3434
"return-content" => Some(response.clone().try_into()?),
3535
_ => {

sdk/iot_hub/src/service/responses/query_response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl std::convert::TryFrom<crate::service::CollectedResponse> for QueryResponse
2222
Ok(QueryResponse {
2323
result: serde_json::from_slice(body)?,
2424
continuation_token: continuation_token_from_headers_optional(headers)?,
25-
item_type: headers.get_as_string_or_err(&headers::ITEM_TYPE)?,
25+
item_type: headers.get_string(&headers::ITEM_TYPE)?,
2626
})
2727
}
2828
}

sdk/messaging_servicebus/src/service_bus/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ async fn peek_lock_message2(
173173
let status = res.status();
174174
let lock_location = res
175175
.headers()
176-
.get_as_string(&headers::LOCATION)
176+
.get_optional_string(&headers::LOCATION)
177177
.unwrap_or_default();
178178
let body = body_bytes_to_utf8(&res.into_body().await)?;
179179

sdk/storage/src/account/operations/find_blobs_by_tags.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ impl FindBlobsByTagsBuilder {
3333
next_marker: NextMarker => Some(next_marker),
3434
max_results: MaxResults => Some(max_results),
3535
timeout: Timeout => Some(timeout),
36+
context: Context => context,
3637
}
3738

3839
// TODO: Make this a stream instead of a `Future`

sdk/storage/src/authorization_policy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ fn generate_authorization(
9292
}
9393

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

9898
#[allow(unknown_lints)]
@@ -118,7 +118,7 @@ fn string_to_sign(
118118
// content length must only be specified if != 0
119119
// this is valid from 2015-02-21
120120
let content_length = h
121-
.get_as_str(&CONTENT_LENGTH)
121+
.get_optional_str(&CONTENT_LENGTH)
122122
.filter(|&v| v != "0")
123123
.unwrap_or_default();
124124
format!(
@@ -152,7 +152,7 @@ fn canonicalize_header(headers: &Headers) -> String {
152152
let mut result = String::new();
153153

154154
for header_name in names {
155-
let value = headers.get_as_str(header_name).unwrap();
155+
let value = headers.get_optional_str(header_name).unwrap();
156156
let name = header_name.as_str();
157157
result = format!("{result}{name}:{value}\n");
158158
}

0 commit comments

Comments
 (0)