1use std::{
2 fs::File,
3 io::{self, BufReader, Read},
4 path::PathBuf,
5 str::Chars,
6};
7use tracing::debug;
8
9#[derive(Clone, Debug)]
11pub struct Source {
12 content: String,
13 path: Option<PathBuf>,
14}
15
16impl Source {
17 pub fn from_string(string: String) -> Self {
19 debug!("Creating source from string");
20 Self {
21 content: string,
22 path: None,
23 }
24 }
25
26 pub fn from_bytes<T: AsRef<[u8]> + ?Sized>(source: &T) -> Self {
28 debug!("Creating source from bytes");
29 Self {
30 content: source.as_ref().iter().map(|&b| b as char).collect(),
31 path: None,
32 }
33 }
34
35 pub fn from_path(path: PathBuf) -> io::Result<Self> {
37 debug!("Creating source from path: {:?}", path);
38 let file = File::open(&path)?;
39 let reader = BufReader::new(file);
40 Ok(Self {
41 content: reader
42 .bytes()
43 .filter_map(|b| b.ok().map(|b| b as char))
44 .collect(),
45 path: Some(path),
46 })
47 }
48
49 pub fn with_path(self, new_path: PathBuf) -> Self {
51 Self {
52 content: self.content,
53 path: Some(new_path),
54 }
55 }
56
57 pub fn content(&self) -> String {
59 self.content.clone()
60 }
61
62 pub fn path(&self) -> Option<PathBuf> {
64 self.path.clone()
65 }
66
67 pub fn len(&self) -> usize {
69 self.content.len()
70 }
71
72 pub fn chars(&self) -> Chars {
74 self.content.chars()
75 }
76
77 pub fn get_between(&self, start: usize, end: usize) -> String {
79 self.content[start..end].to_string()
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_source_from_string() {
89 let source = Source::from_string("fn main() {}".to_string());
90
91 assert_eq!(source.content(), "fn main() {}");
92 assert_eq!(source.path(), None);
93 }
94
95 #[test]
96 fn test_source_from_bytes() {
97 let source = Source::from_bytes(b"fn main() {}");
98
99 assert_eq!(source.content(), "fn main() {}");
100 assert_eq!(source.path(), None);
101 }
102
103 #[test]
104 fn test_source_with_path() {
105 let source = Source::from_string("fn main() {}".to_string())
106 .with_path(PathBuf::from("tests/test.roan"));
107
108 assert_eq!(source.content(), "fn main() {}");
109 assert_eq!(source.path(), Some(PathBuf::from("tests/test.roan")));
110 }
111
112 #[test]
113 fn test_source_len() {
114 let source = Source::from_string("fn main() {}".to_string());
115
116 assert_eq!(source.len(), 12);
117 }
118
119 #[test]
120 fn test_source_chars() {
121 let source = Source::from_string("fn main() {}".to_string());
122
123 assert_eq!(source.chars().collect::<String>(), "fn main() {}");
124 }
125
126 #[test]
127 fn test_source_get_between() {
128 let source = Source::from_string("fn main() {}".to_string());
129
130 assert_eq!(source.get_between(3, 7), "main");
131 }
132}