use std::fmt;
use {
Stream,
};
#[must_use]
#[derive(PartialEq, Clone, Copy)]
pub struct StrSpan<'a> {
text: &'a str,
start: usize,
end: usize,
}
impl<'a> From<&'a str> for StrSpan<'a> {
fn from(text: &'a str) -> Self {
StrSpan {
text,
start: 0,
end: text.len(),
}
}
}
impl<'a> StrSpan<'a> {
pub fn from_substr(text: &str, start: usize, end: usize) -> StrSpan {
debug_assert!(start <= end);
debug_assert!(text.is_char_boundary(start));
debug_assert!(text.is_char_boundary(end));
StrSpan { text, start, end }
}
pub fn start(&self) -> usize {
self.start
}
pub fn end(&self) -> usize {
self.end
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn full_len(&self) -> usize {
self.text.len()
}
pub fn to_str(&self) -> &'a str {
&self.text[self.start..self.end]
}
pub fn as_bytes(&self) -> &'a [u8] {
&self.text.as_bytes()[self.start..self.end]
}
pub fn slice_region(&self, start: usize, end: usize) -> StrSpan<'a> {
let start = self.start + start;
let end = self.start + end;
StrSpan::from_substr(self.text, start, end)
}
pub fn full_str(&self) -> &'a str {
self.text
}
pub fn trim(&self) -> StrSpan<'a> {
let mut s = Stream::from(*self);
s.skip_spaces();
let start = s.pos();
let mut end;
loop {
s.skip_bytes(|s, _| !s.starts_with_space());
end = s.pos();
s.skip_spaces();
if s.at_end() {
break;
}
}
self.slice_region(start, end)
}
}
impl<'a> fmt::Debug for StrSpan<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "StrSpan({:?} {}..{})", self.to_str(), self.start, self.end)
}
}
impl<'a> fmt::Display for StrSpan<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trim_1() {
assert_eq!(StrSpan::from(" text ").trim().to_str(), "text");
}
#[test]
fn trim_2() {
assert_eq!(StrSpan::from(" text text ").trim().to_str(), "text text");
}
#[test]
fn trim_3() {
assert_eq!(StrSpan::from(" text ").trim().to_str(), "text");
}
#[test]
fn trim_4() {
assert_eq!(StrSpan::from(" text text ").trim().to_str(), "text text");
}
#[test]
fn do_not_trim_1() {
assert_eq!(StrSpan::from("@textP").trim().to_str(), "@textP");
}
#[test]
fn do_not_trim_2() {
assert_eq!(StrSpan::from("&ref;text'").trim().to_str(), "&ref;text'");
}
}