#![warn(missing_docs)]
use std::convert::AsRef;
use std::fmt::{Debug, Display};
use std::ops::{Deref, Range};
use std::rc::Rc;
#[derive(Debug)]
pub struct RcSubstring {
rcstring: Rc<String>,
range: Range<usize>,
}
impl Display for RcSubstring {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.deref())
}
}
impl PartialEq<&str> for RcSubstring {
fn eq(&self, other: &&str) -> bool {
self.deref() == *other
}
}
impl RcSubstring {
pub fn new(rcstring: Rc<String>, range: Range<usize>) -> Self {
debug_assert!(
range.end >= range.start,
"begin < end ({} < {}) when creating RcSubstring",
range.start,
range.end
);
debug_assert!(
range.start <= rcstring.len(),
"start index {} out of bounds when creating RcSubstring",
range.start
);
debug_assert!(
range.end <= rcstring.len(),
"end index {} out of bounds when creating RcSubstring",
range.end
);
RcSubstring { rcstring, range }
}
}
impl Deref for RcSubstring {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.rcstring[self.range.start..self.range.end]
}
}
impl<T> AsRef<T> for RcSubstring
where
T: ?Sized,
<RcSubstring as Deref>::Target: AsRef<T>,
{
fn as_ref(&self) -> &T {
self.deref().as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_usage() {
let text = "Line 1\nLine 2\nLine 3";
let rcstring = Rc::new(text.to_string());
let pos = text.find("\n").unwrap();
let rcsubstring = RcSubstring::new(rcstring.clone(), 0..pos);
let string_rep = format!("{}", rcsubstring);
assert_eq!(string_rep, "Line 1");
let debug_rep = format!("{:?}", rcsubstring);
assert_eq!(
debug_rep,
"RcSubstring { rcstring: \"Line 1\\nLine 2\\nLine 3\", range: 0..6 }"
);
let pretty_rep = format!("{:#?}", rcsubstring);
assert_eq!(
pretty_rep,
"RcSubstring {\n rcstring: \"Line 1\\nLine 2\\nLine 3\",\n range: 0..6,\n}"
);
assert_eq!(&rcsubstring[1..2], "i");
}
#[test]
fn test_empty() {
let rcsubstring = RcSubstring::new(Rc::new(String::from("Random text")), 3..3);
assert_eq!(rcsubstring.len(), 0);
assert_eq!(rcsubstring, "");
}
#[test]
fn test_as_ref() {
fn is_hello<T: AsRef<str>>(s: T) {
assert_eq!(s.as_ref(), "hello");
}
let text = String::from("hello world!");
let rcss = RcSubstring::new(Rc::new(text), 0..5);
is_hello(rcss);
}
#[test]
#[should_panic(expected = "RcSubstring")]
fn test_end_before_start() {
let _ = RcSubstring::new(Rc::new(String::from("Random text")), 3..0);
}
#[test]
#[should_panic(expected = "RcSubstring")]
fn test_start_out_of_range() {
let _ = RcSubstring::new(Rc::new(String::from("Random text")), 100..101);
}
#[test]
#[should_panic(expected = "RcSubstring")]
fn test_end_out_of_range() {
let _ = RcSubstring::new(Rc::new(String::from("Random text")), 0..101);
}
}