lemonlang 0.0.4

an experimental, modern, purely safe, programming language.
use std::path::PathBuf;

use rustc_hash::FxHashMap;

use crate::{report::throw_error, source::Source};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FileId(u64);

impl FileId {
	pub fn new(id: u64) -> Self {
		Self(id)
	}
	pub fn as_usize(&self) -> usize {
		self.0 as usize
	}
}

pub struct Loader {
	sources: FxHashMap<FileId, Source>,
}

impl Loader {
	pub fn new() -> Self {
		Self { sources: FxHashMap::default() }
	}

	fn validate_extension(&self, ext: Option<&str>) {
		if let Some(ext) = ext {
			if ext != "ln" && ext != "lemon" {
				throw_error("unknown file extension, expected 'ln'")
			}
		} else {
			throw_error("extention not found, expected 'ln'")
		}
	}

	pub fn load(&mut self, path_str: &str) -> FileId {
		let path = PathBuf::from(path_str);
		let ext = path.extension().and_then(|ext| ext.to_str());
		self.validate_extension(ext);
		let file_id = self.add_file(path);
		self.load_file(file_id);
		file_id
	}

	pub fn load_file(&mut self, file_id: FileId) {
		let path = self.get_file(file_id);
		let raw = std::fs::read_to_string(path).unwrap_or_else(|err| match err.kind() {
			std::io::ErrorKind::NotFound => throw_error(format!("not found '{}'.", path.display())),
			_ => throw_error(format!("reading file `{}`, reason '{}'.", path.display(), err)),
		});
		let source = Source::new(raw, path.to_owned());
		self.sources.insert(file_id, source);
	}

	pub fn get_file(&self, file_id: FileId) -> &PathBuf {
		match self.sources.get(&file_id).map(|source| &source.pathbuf) {
			Some(path) => path,
			None => throw_error(format!("file not found file_{}", file_id.as_usize())),
		}
	}

	pub fn add_file(&mut self, path: PathBuf) -> FileId {
		let file_id = FileId::new(self.sources.len() as u64);
		self.sources.insert(file_id, Source::new(String::new(), path));
		file_id
	}

	pub fn get_source(&self, file_id: FileId) -> &Source {
		match self.sources.get(&file_id) {
			Some(source) => source,
			None => throw_error(format!("file not found file_{}", file_id.as_usize())),
		}
	}
}

impl Default for Loader {
	fn default() -> Self {
		Self::new()
	}
}