ccs2/
load_helper.rs

1use std::path::{Path, PathBuf};
2
3use crate::{AstError, AstResult, ast::ImportResolver};
4
5/// An error that occurs while reading a CCS file, before it gets parsed
6///
7/// See [`IoResult`]
8#[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
30/// The default import resolver, which resolves everything relative to one initial path
31pub struct RelativePathResolver(PathBuf);
32impl RelativePathResolver {
33    /// A convenience function for creating based on a specific path, such as the `main.ccs`
34    ///
35    /// Note that the source should be an absolute path, but this will then allow for resolving
36    /// relative to the parent directory of the given file.
37    ///
38    /// If the source isn't absolute, it will go relative to the current working directory. However,
39    /// subsequent resolvers created through [`ImportResolver::new_context`] _will_ be absolute.
40    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}