use crate::ids::Id;
pub type FileId = Id<SourceFile>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceFile {
pub path: String,
}
impl SourceFile {
pub fn new(path: impl Into<String>) -> Self {
SourceFile { path: path.into() }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Position {
pub line: u32,
pub col: u32,
}
impl Position {
pub const fn new(line: u32, col: u32) -> Self {
Position { line, col }
}
pub const fn is_valid(self) -> bool {
self.line >= 1 && self.col >= 1
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub file: FileId,
pub start: Position,
pub end: Position,
}
impl Span {
pub const fn new(file: FileId, start: Position, end: Position) -> Self {
Span { file, start, end }
}
pub const fn is_valid(self) -> bool {
self.start.is_valid()
&& self.end.is_valid()
&& (self.end.line > self.start.line
|| (self.end.line == self.start.line && self.end.col >= self.start.col))
}
}
#[cfg(test)]
mod tests {
use super::{Position, SourceFile, Span};
use crate::ids::Id;
#[test]
fn positions_are_one_based_and_validated() {
assert!(Position::new(1, 1).is_valid());
assert!(Position::new(10, 24).is_valid());
assert!(!Position::new(0, 1).is_valid());
assert!(!Position::new(1, 0).is_valid());
}
#[test]
fn spans_require_end_not_before_start() {
let file = Id::from_index(0);
assert!(Span::new(file, Position::new(1, 1), Position::new(1, 5)).is_valid());
assert!(Span::new(file, Position::new(1, 1), Position::new(2, 1)).is_valid());
assert!(Span::new(file, Position::new(1, 1), Position::new(1, 1)).is_valid());
assert!(!Span::new(file, Position::new(1, 5), Position::new(1, 1)).is_valid());
assert!(!Span::new(file, Position::new(2, 1), Position::new(1, 1)).is_valid());
}
#[test]
fn source_files_carry_paths() {
let file = SourceFile::new("source.opy");
assert_eq!(file.path, "source.opy");
}
}