use ::std::fmt;
use std::rc::Rc;
#[derive(PartialEq, Eq, Clone)]
pub struct SourceFile {
content: Rc<SourceFileContent>,
}
#[derive(Eq)]
pub struct SourceFileContent {
source_identifier: String,
data: String,
}
impl From<SourceFileContent> for SourceFile {
fn from(content: SourceFileContent) -> Self {
SourceFile { content: Rc::new(content) }
}
}
impl SourceFile {
pub fn new(source_identifier: impl Into<String>, text: impl Into<String>) -> Self {
SourceFileContent {
source_identifier: source_identifier.into(),
data: text.into()
}.into()
}
#[cfg(test)]
pub fn test(text: impl Into<String>) -> Self {
SourceFileContent {
source_identifier: "for_test".to_owned(),
data: text.into()
}.into()
}
pub fn identifier(&self) -> &str {
&self.content.source_identifier
}
pub fn text(&self) -> &str {
&self.content.data
}
pub fn len(&self) -> usize {
self.content.data.len()
}
pub fn slice(&self, start: usize, end: usize) -> SourceSlice {
SourceSlice::new(self, start, end)
}
pub fn slice_from(&self, start: usize) -> SourceSlice {
SourceSlice::new(self, start, self.len())
}
}
impl fmt::Debug for SourceFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SourceFile{{ at '{}' length {}}}", self.content.source_identifier, self.len())
}
}
impl PartialEq for SourceFileContent {
fn eq(&self, other: &Self) -> bool {
if self.source_identifier == other.source_identifier {
debug_assert!(self.data == other.data);
return true
}
false
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct SourceSlice {
file: SourceFile,
start: usize,
end: usize
}
impl SourceSlice {
pub fn new(file: &SourceFile, start: usize, end: usize) -> Self {
debug_assert!(end >= start);
debug_assert!(end <= file.content.data.len());
SourceSlice { file: file.clone(), start, end }
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn as_str(&self) -> &str {
&self.file.text()[self.start .. self.end]
}
}
impl fmt::Debug for SourceSlice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SourceSlice{{ of '{:?}' [{}..{}]: {}}}", self.file.identifier(), self.start, self.end, self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slice_str() {
let f = SourceFile::test("hello world!");
assert_eq!(f.slice(3, 7).as_str(), "lo w");
}
#[test]
fn test_slice_from_str() {
let f = SourceFile::test("hello world!");
assert_eq!(f.slice_from(6).as_str(), "world!");
}
#[test]
fn test_slice_empty() {
let f = SourceFile::test("hello world!");
assert_eq!(f.slice(3, 3).as_str(), "");
}
#[test]
fn test_slice_all() {
let f = SourceFile::test("hello world!");
assert_eq!(f.slice(0, 12).as_str(), "hello world!");
}
#[test]
fn test_slice_eq() {
let f = SourceFile::test("hello world!");
assert_eq!(SourceSlice::new(&f, 3, 7), f.slice(3, 7));
}
#[test]
fn test_slice_neq_file() {
let f1 = SourceFile::new("a.txt", "hello world!");
let f2 = SourceFile::new("b.txt", "hello world!");
assert_ne!(f2.slice(3, 7), f1.slice(3, 7));
}
#[test]
fn test_slice_neq_start() {
let f = SourceFile::new("a.txt", "hello world!");
assert_ne!(f.slice(3, 7), f.slice(2, 7));
}
#[test]
fn test_slice_neq_end() {
let f = SourceFile::new("a.txt", "hello world!");
assert_ne!(f.slice(3, 6), f.slice(3, 7));
}
}