-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(headers): factor out EntityTag from Etag
Allow use of EntityTag in other headers that use entity tags. BREAKING CHANGE: for any consumers of the Etag header, since the entity tag is now in a tuple.
- Loading branch information
1 parent
c2784bc
commit 28fd5c8
Showing
4 changed files
with
166 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
use std::str::FromStr; | ||
use std::fmt::{self, Display}; | ||
|
||
/// An entity tag | ||
/// | ||
/// An Etag consists of a string enclosed by two literal double quotes. | ||
/// Preceding the first double quote is an optional weakness indicator, | ||
/// which always looks like this: W/ | ||
/// See also: https://tools.ietf.org/html/rfc7232#section-2.3 | ||
#[derive(Clone, PartialEq, Debug)] | ||
pub struct EntityTag { | ||
/// Weakness indicator for the tag | ||
pub weak: bool, | ||
/// The opaque string in between the DQUOTEs | ||
pub tag: String | ||
} | ||
|
||
impl Display for EntityTag { | ||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
if self.weak { | ||
try!(write!(fmt, "{}", "W/")); | ||
} | ||
write!(fmt, "{}", self.tag) | ||
} | ||
} | ||
|
||
// check that each char in the slice is either: | ||
// 1. %x21, or | ||
// 2. in the range %x23 to %x7E, or | ||
// 3. in the range %x80 to %xFF | ||
fn check_slice_validity(slice: &str) -> bool { | ||
for c in slice.bytes() { | ||
match c { | ||
b'\x21' | b'\x23' ... b'\x7e' | b'\x80' ... b'\xff' => (), | ||
_ => { return false; } | ||
} | ||
} | ||
true | ||
} | ||
|
||
impl FromStr for EntityTag { | ||
type Err = (); | ||
fn from_str(s: &str) -> Result<EntityTag, ()> { | ||
let length: usize = s.len(); | ||
let slice = &s[]; | ||
|
||
// Early exits: | ||
// 1. The string is empty, or, | ||
// 2. it doesn't terminate in a DQUOTE. | ||
if slice.is_empty() || !slice.ends_with("\"") { | ||
return Err(()); | ||
} | ||
|
||
// The etag is weak if its first char is not a DQUOTE. | ||
if slice.char_at(0) == '"' /* '"' */ { | ||
// No need to check if the last char is a DQUOTE, | ||
// we already did that above. | ||
if check_slice_validity(slice.slice_chars(1, length-1)) { | ||
return Ok(EntityTag { | ||
weak: false, | ||
tag: slice.slice_chars(1, length-1).to_string() | ||
}); | ||
} else { | ||
return Err(()); | ||
} | ||
} | ||
|
||
if slice.slice_chars(0, 3) == "W/\"" { | ||
if check_slice_validity(slice.slice_chars(3, length-1)) { | ||
return Ok(EntityTag { | ||
weak: true, | ||
tag: slice.slice_chars(3, length-1).to_string() | ||
}); | ||
} else { | ||
return Err(()); | ||
} | ||
} | ||
|
||
Err(()) | ||
} | ||
} | ||
|
||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::EntityTag; | ||
|
||
#[test] | ||
fn test_etag_successes() { | ||
// Expected successes | ||
let mut etag : EntityTag = "\"foobar\"".parse().unwrap(); | ||
assert_eq!(etag, (EntityTag { | ||
weak: false, | ||
tag: "foobar".to_string() | ||
})); | ||
|
||
etag = "\"\"".parse().unwrap(); | ||
assert_eq!(etag, EntityTag { | ||
weak: false, | ||
tag: "".to_string() | ||
}); | ||
|
||
etag = "W/\"weak-etag\"".parse().unwrap(); | ||
assert_eq!(etag, EntityTag { | ||
weak: true, | ||
tag: "weak-etag".to_string() | ||
}); | ||
|
||
etag = "W/\"\x65\x62\"".parse().unwrap(); | ||
assert_eq!(etag, EntityTag { | ||
weak: true, | ||
tag: "\u{0065}\u{0062}".to_string() | ||
}); | ||
|
||
etag = "W/\"\"".parse().unwrap(); | ||
assert_eq!(etag, EntityTag { | ||
weak: true, | ||
tag: "".to_string() | ||
}); | ||
} | ||
|
||
#[test] | ||
fn test_etag_failures() { | ||
// Expected failures | ||
let mut etag: Result<EntityTag,()>; | ||
|
||
etag = "no-dquotes".parse(); | ||
assert_eq!(etag, Err(())); | ||
|
||
etag = "w/\"the-first-w-is-case-sensitive\"".parse(); | ||
assert_eq!(etag, Err(())); | ||
|
||
etag = "".parse(); | ||
assert_eq!(etag, Err(())); | ||
|
||
etag = "\"unmatched-dquotes1".parse(); | ||
assert_eq!(etag, Err(())); | ||
|
||
etag = "unmatched-dquotes2\"".parse(); | ||
assert_eq!(etag, Err(())); | ||
|
||
etag = "matched-\"dquotes\"".parse(); | ||
assert_eq!(etag, Err(())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
pub use self::encoding::Encoding; | ||
pub use self::entity::EntityTag; | ||
pub use self::quality_item::{QualityItem, qitem}; | ||
|
||
mod encoding; | ||
mod entity; | ||
mod quality_item; |