use std::fs::File;
use std::io::*;
use crate::compiling::{Metadata, Token};
#[derive(Debug, Clone)]
pub enum Position {
Pos(usize, usize),
EOF
}
#[derive(Debug, Clone)]
pub struct PositionInfo {
pub path: Option<String>,
pub position: Position,
pub row: usize,
pub col: usize,
pub len: usize,
pub data: Option<String>
}
impl PositionInfo {
pub fn new(meta: &impl Metadata, position: Position, len: usize) -> Self {
PositionInfo {
position,
path: meta.get_path(),
row: 0,
col: 0,
len,
data: None
}.updated_pos(meta)
}
pub fn at_eof(meta: &impl Metadata) -> Self {
PositionInfo {
path: meta.get_path(),
position: Position::EOF,
row: 0,
col: 0,
len: 0,
data: None
}.updated_pos(meta)
}
pub fn at_pos(path: Option<String>, (row, col): (usize, usize), len: usize) -> Self {
PositionInfo {
path,
position: Position::Pos(row, col),
row,
col,
len,
data: None
}
}
fn updated_pos(mut self, meta: &impl Metadata) -> Self {
(self.row, self.col) = self.get_pos_by_file_or_code(meta.get_code());
self
}
pub fn get_path(&self) -> String {
self.path.clone().unwrap_or_else(|| "[unknown]".to_string())
}
pub fn from_metadata(meta: &impl Metadata) -> Self {
Self::from_token(meta, meta.get_current_token())
}
pub fn from_token(meta: &impl Metadata, token_opt: Option<Token>) -> Self {
match token_opt {
Some(token) => PositionInfo::at_pos(meta.get_path(), token.pos, token.word.chars().count()),
None => PositionInfo::at_eof(meta)
}
}
pub fn data<T: AsRef<str>>(mut self, data: T) -> Self {
self.data = Some(data.as_ref().to_string());
self
}
pub fn get_pos_by_file_or_code(&self, code: Option<&String>) -> (usize, usize) {
match self.position {
Position::Pos(row, col) => (row, col),
Position::EOF => {
if let Some(code) = code {
self.get_pos_by_code(code)
}
else if let Some(path) = &self.path {
match self.get_pos_by_file(path) {
Ok((row, col)) => (row, col),
Err(_) => (0, 0)
}
}
else { (0, 0) }
}
}
}
pub fn get_pos_by_file(&self, path: impl AsRef<str>) -> std::io::Result<(usize, usize)> {
let mut code = String::new();
let mut file = File::open(path.as_ref())?;
file.read_to_string(&mut code)?;
Ok(self.get_pos_by_code(&code))
}
pub fn get_pos_by_code(&self, code: impl AsRef<str>) -> (usize, usize) {
let code = code.as_ref();
match self.position {
Position::Pos(row, col) => (row, col),
Position::EOF => {
let mut col = 1;
let mut row = 1;
col += code.split_whitespace().count();
if let Some(last) = code.split_whitespace().last() {
row += last.len();
}
(row, col)
}
}
}
}