chronlang_engine/
resolver.rs1use std::{collections::HashMap, path::Path, fs};
2
3#[derive(Debug, PartialEq, Clone)]
4pub enum ResolutionError {
5 InvalidPathForResolver(String),
6 PathNotFound(String),
7}
8
9impl ToString for ResolutionError {
10 fn to_string(&self) -> String {
11 match self {
12 ResolutionError::InvalidPathForResolver(reason) => format!("{reason}. Try using a different resolver."),
13 ResolutionError::PathNotFound(path) => format!("Failed to resolve path `{path}`."),
14 }
15 }
16}
17
18pub trait Resolve {
19 fn resolve(&self, path: &[&str]) -> Result<(String, String), ResolutionError>;
20}
21
22#[derive(Debug, PartialEq)]
23pub struct FileSystemResolver<'a> {
24 base_path: &'a Path,
25 cache: HashMap<&'a str, String>,
26}
27
28impl FileSystemResolver<'_> {
29 pub fn new(base_path: &Path) -> FileSystemResolver<'_> {
30 FileSystemResolver { base_path, cache: HashMap::new() }
31 }
32}
33
34impl Resolve for FileSystemResolver<'_> {
35 fn resolve(&self, path: &[&str]) -> Result<(String, String), ResolutionError> {
36 match path.get(0) {
37 Some(segment) if segment.starts_with('@') => {
38 return Err(ResolutionError::InvalidPathForResolver("FileSystemResolver cannot resolve remote imports.".into()))
39 },
40 _ => {}
41 }
42
43 let mut dir = self.base_path.join(path.join("/"));
44 dir.set_extension("lang");
45 let file_name = dir.to_str().unwrap();
46
47 if let Some(value) = self.cache.get(file_name) {
48 return Ok((value.clone(), file_name.to_string()))
49 }
50
51 let file = fs::read_to_string(dir.clone());
52 match file {
53 Ok(contents) => Ok((contents, file_name.to_string())),
54 _ => Err(ResolutionError::PathNotFound(file_name.to_string())),
55 }
56 }
57}
58
59#[derive(Debug, PartialEq)]
60pub struct MockResolver {
61 sources: HashMap<String, String>,
62}
63
64impl MockResolver {
65 pub fn new(sources: HashMap<String, String>) -> MockResolver {
66 MockResolver { sources }
67 }
68}
69
70impl Resolve for MockResolver {
71 fn resolve(&self, path: &[&str]) -> Result<(String, String), ResolutionError> {
72 let path = path.join("/");
73 match self.sources.get(&path) {
74 Some(source) => Ok((source.clone(), path)),
75 None => Err(ResolutionError::PathNotFound(path)),
76 }
77 }
78}
79
80
81#[cfg(test)]
82mod test {
83 use super::*;
84
85 #[test]
86 fn file_system_resolver_resolves_an_existing_path() {
87 let base_path = Path::new("./");
88 let resolver = FileSystemResolver::new(base_path);
89
90 assert!(resolver.resolve(&["demo"]).is_ok());
91 }
92
93 #[test]
94 fn file_system_resolver_rejects_an_invalid_path() {
95 let base_path = Path::new("./");
96 let resolver = FileSystemResolver::new(base_path);
97
98 assert_eq!(
99 resolver.resolve(&["invalid"]),
100 Err(ResolutionError::PathNotFound("./invalid.lang".into())),
101 );
102
103 }
104}