1use std::path::{Path, PathBuf};
2
3use crate::{AstError, AstResult, ast::ImportResolver};
4
5#[derive(thiserror::Error, Debug)]
9pub enum IoError {
10 #[error("Failed to find file {0:?}")]
11 FileNotFound(std::path::PathBuf),
12 #[error(transparent)]
13 Other(#[from] std::io::Error),
14}
15pub type IoResult<T> = Result<T, IoError>;
16
17pub fn load(path: impl AsRef<Path>) -> IoResult<String> {
18 use std::io::ErrorKind::*;
19
20 let path = path.as_ref();
21 match std::fs::read_to_string(path) {
22 Ok(content) => Ok(content),
23 Err(e) => match e.kind() {
24 NotFound => Err(IoError::FileNotFound(path.into())),
25 _ => Err(IoError::Other(e)),
26 },
27 }
28}
29
30pub struct RelativePathResolver(PathBuf);
32impl RelativePathResolver {
33 pub fn siblings_with(file: impl AsRef<Path>) -> AstResult<Self> {
41 let path: PathBuf = file.as_ref().into();
42 if !path.is_file() {
43 Err(AstError::ImportFailed(path))
44 } else {
45 Ok(Self(path))
46 }
47 }
48}
49impl ImportResolver for RelativePathResolver {
50 fn current_file_name(&self) -> PathBuf {
51 self.0.clone()
52 }
53
54 fn new_context(&self, location: &Path) -> AstResult<Self> {
55 let new_file = self
56 .0
57 .parent()
58 .ok_or(AstError::ImportFailed(location.into()))?
59 .join(location);
60 Self::siblings_with(new_file)
61 }
62
63 fn load(&self) -> AstResult<String> {
64 std::fs::read_to_string(&self.0).map_err(|_| AstError::ImportFailed(self.0.clone()))
65 }
66}