use crate::sources::character_class::CharacterClass;
use miette::{MietteError, SourceCode, SourceSpan, SpanContents};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::io;
use std::io::Read;
use std::iter::Peekable;
use std::path::Path;
use std::sync::Arc;
#[doc(hidden)]
#[derive(Debug, Serialize, Deserialize)]
struct Inner {
contents: String,
contents_for_display: String,
name: String,
}
#[derive(Clone, Debug)]
pub struct SourceFile(Arc<Inner>);
impl Serialize for SourceFile {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_unit()
}
}
impl<'de> Deserialize<'de> for SourceFile {
fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(SourceFile::new("", "dummmy"))
}
}
impl SourceFile {
pub fn open(name: impl AsRef<Path>) -> io::Result<Self> {
let mut f = std::fs::File::open(&name)?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
Ok(Self(Arc::new(Inner {
contents: contents.clone(),
contents_for_display: contents + " ",
name: name.as_ref().to_string_lossy().to_string(),
})))
}
pub fn new(contents: impl AsRef<str>, name: impl AsRef<str>) -> Self {
Self(Arc::new(Inner {
contents: contents.as_ref().to_string(),
contents_for_display: contents.as_ref().to_string() + " ",
name: name.as_ref().to_string(),
}))
}
pub fn new_for_test(s: impl AsRef<str>) -> Self {
Self::new(s.as_ref(), "test")
}
pub fn iter(&self) -> SourceFileIterator {
SourceFileIterator {
inner_iter: self.0.contents.chars().peekable(),
index: 0,
}
}
pub fn name(&self) -> &str {
&self.0.name
}
pub fn contents(&self) -> &str {
&self.0.contents
}
pub fn contents_for_display(&self) -> &str {
&self.0.contents_for_display
}
}
#[derive(Clone)]
pub struct SourceFileIterator<'a> {
inner_iter: Peekable<std::str::Chars<'a>>,
index: usize,
}
impl<'a> SourceFileIterator<'a> {
pub fn peek(&mut self) -> Option<&char> {
self.inner_iter.peek()
}
pub fn advance(&mut self) {
self.next();
}
pub fn skip_n(&mut self, n: usize) {
for _ in 0..n {
self.advance();
}
}
pub fn max_pos(&mut self, other: Self) {
if other.index > self.index {
*self = other;
}
}
pub fn accept(&mut self, c: &CharacterClass) -> bool {
self.accept_option(c).is_some()
}
pub fn accept_option(&mut self, c: &CharacterClass) -> Option<char> {
if let Some(true) = self.peek().map(|&i| c.contains(i)) {
self.next()
} else {
None
}
}
pub fn accept_str(&mut self, s: &str) -> bool {
let mut self_clone = self.clone();
for c in s.chars() {
if !self_clone.accept(&c.into()) {
return false;
}
}
*self = self_clone;
true
}
pub fn skip_layout(&mut self, layout: &CharacterClass) {
while self.accept(layout) {}
}
pub fn accept_skip_layout(&mut self, c: &CharacterClass, layout: &CharacterClass) -> bool {
let mut self_clone = self.clone();
self_clone.skip_layout(layout);
if self_clone.accept(c) {
*self = self_clone;
true
} else {
false
}
}
pub fn accept_str_skip_layout(&mut self, s: &str, layout: &CharacterClass) -> bool {
let mut self_clone = self.clone();
self_clone.skip_layout(layout);
if self_clone.accept_str(s) {
*self = self_clone;
true
} else {
false
}
}
pub fn accept_to_next(&mut self, target: &CharacterClass) -> String {
let mut res = String::new();
while let Some(&i) = self.peek() {
if target.contains(i) {
break;
} else {
res.push(i);
self.advance();
}
}
res
}
pub fn exhausted(&mut self) -> bool {
self.peek().is_none()
}
pub fn position(&self) -> usize {
self.index
}
}
impl<'a> Iterator for SourceFileIterator<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
let next = self.inner_iter.next();
if let Some(next) = next {
self.index += next.len_utf8();
}
next
}
}
impl SourceCode for SourceFile {
fn read_span<'a>(
&'a self,
span: &SourceSpan,
context_lines_before: usize,
context_lines_after: usize,
) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
<str as SourceCode>::read_span(
self.contents(),
span,
context_lines_before,
context_lines_after,
)
}
}