use std::{io::ErrorKind, path::Path};
use anyhow::{Context, Error};
use diskit::{diskit_extend::DiskitExt, Diskit};
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct Pos
{
pub line: u32,
pub column: u32,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct Span
{
pub start: Pos,
pub end: Pos,
}
impl Default for Pos
{
fn default() -> Self
{
Self { line: 1, column: 0 }
}
}
impl Pos
{
pub const fn update(&mut self, c: char)
{
if c == '\n'
{
self.line += 1;
self.column = 0;
}
else
{
self.column += 1;
}
}
}
fn is_same_start(a: &[char], b: &str) -> bool
{
a.iter().zip(b.chars()).all(|(&x, y)| x == y)
}
pub fn starts_with(a: &[char], b: &str) -> bool
{
is_same_start(a, b) && a.len() >= b.chars().count()
}
pub fn is_eq(a: &[char], b: &str) -> bool
{
is_same_start(a, b) && a.len() == b.chars().count()
}
#[allow(clippy::needless_pass_by_value)]
pub fn try_read_to_string<D: Diskit>(path: &Path, d: D) -> Result<Option<String>, Error>
{
match d.read_to_string(path)
{
Ok(s) => Ok(Some(s)),
Err(e) if e.kind() == ErrorKind::NotFound => Ok(None),
Err(e) => Err(e).with_context(|| format!("Couldn't read {path:?}")),
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn read_to_string<D: Diskit>(path: &Path, d: D) -> Result<String, Error>
{
if let Some(s) = try_read_to_string(path, d.clone()).context("Couldn't read file to string")?
{
return Ok(s);
}
let mut path = path
.to_str()
.context("Input file doesn't exist and its path is not UTF8")?
.to_string();
#[allow(clippy::case_sensitive_file_extension_comparisons)]
if path.ends_with(".t")
{
path.push_str("ex");
d.read_to_string(&path).context("Couldn't read path with added \"ex\"")
}
else if path.ends_with('.')
{
path.push_str("tex");
d.read_to_string(&path).context("Couldn't read path with added \"tex\"")
}
else
{
path.push_str(".tex");
d.read_to_string(&path)
.context("Couldn't read path with added \".tex\"")
}
}
#[cfg(test)]
mod tests
{
use crate::misc::Pos;
#[test]
fn pos_test()
{
let tests = vec![
("abc\ndef", vec![(1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2)]),
("äöü\nÄÖÜ", vec![(1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2)]),
(
"\n\n\na\n\nb",
vec![(1, 0), (2, 0), (3, 0), (4, 0), (4, 1), (5, 0), (6, 0)],
),
(
"\n\n\na\n\nb\n",
vec![(1, 0), (2, 0), (3, 0), (4, 0), (4, 1), (5, 0), (6, 0), (6, 1)],
),
(
"\n\n\na\n\nb\n\n",
vec![(1, 0), (2, 0), (3, 0), (4, 0), (4, 1), (5, 0), (6, 0), (6, 1), (7, 0)],
),
];
for (string, poses) in tests
{
let mut pos = Pos::default();
for (c, &(line, column)) in string.chars().zip(poses.iter())
{
assert_eq!(pos, Pos { line, column });
pos.update(c);
}
assert_eq!(string.chars().count(), poses.len());
}
}
}