Skip to main content

huff_neo_utils/file/
file_provider.rs

1use crate::file::file_source::FileSource;
2use crate::file::unpack_files::{UnpackError, unpack_files};
3use crate::{error::CompilerError, time};
4use std::{
5    collections::HashMap,
6    fmt::Debug,
7    path::{Path, PathBuf},
8    sync::Arc,
9};
10
11/// Provides functions to supply file contents by paths.
12pub trait FileProvider: Send + Sync + Debug {
13    /// Returns a FileSource containing the file contents referred to by the supplied path.
14    fn read_file(&self, pb: PathBuf) -> Result<Arc<FileSource>, CompilerError>;
15
16    /// Takes a list of strings and returns a transformed list PathBufs.
17    fn transform_paths(&self, sources: &[String]) -> Result<Vec<PathBuf>, CompilerError>;
18}
19
20/// A FileReader that reads files from the filesystem.
21#[derive(Debug)]
22pub struct FileSystemFileProvider {}
23
24impl Default for FileSystemFileProvider {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl FileSystemFileProvider {
31    /// Creates a new instance of a FileSystemFileReader.
32    pub fn new() -> Self {
33        FileSystemFileProvider {}
34    }
35}
36
37impl FileProvider for FileSystemFileProvider {
38    fn read_file(&self, pb: PathBuf) -> Result<Arc<FileSource>, CompilerError> {
39        let file_loc = String::from(pb.to_string_lossy());
40        match std::fs::read_to_string(&file_loc) {
41            Ok(source) => Ok(Arc::new(FileSource {
42                path: file_loc,
43                source: Some(source),
44                access: Some(time::get_current_time()),
45                dependencies: vec![],
46            })),
47            Err(_) => {
48                tracing::error!(target: "core", "FILE READ FAILED: \"{}\"!", file_loc);
49                Err(CompilerError::FileUnpackError(UnpackError::MissingFile(file_loc)))
50            }
51        }
52    }
53
54    fn transform_paths(&self, sources: &[String]) -> Result<Vec<PathBuf>, CompilerError> {
55        let mut paths = vec![];
56        for f in sources {
57            // If the file is huff, use the path, otherwise unpack
58            let ext = Path::new(&f).extension().unwrap_or_default();
59            if ext.eq("huff") {
60                paths.push(Path::new(&f).to_path_buf())
61            } else {
62                // Otherwise, override the source files and use all files in the provided dir
63                match unpack_files(f) {
64                    Ok(files) => files.iter().for_each(|fil| paths.push(Path::new(&fil).to_path_buf())),
65                    Err(e) => {
66                        tracing::error!(target: "core", "ERROR UNPACKING FILE: {:?}", e);
67                        return Err(CompilerError::FileUnpackError(e));
68                    }
69                }
70            }
71        }
72        Ok(paths)
73    }
74}
75
76/// A FileReader which reads files from memory via a supplied HashMap.
77#[derive(Debug)]
78pub struct InMemoryFileProvider {
79    /// A mapping of paths to file contents.
80    pub sources: Arc<HashMap<String, String>>,
81}
82
83impl InMemoryFileProvider {
84    /// Creates a new instance of a MemoryFileReader.
85    pub fn new(mut sources: HashMap<String, String>) -> Self {
86        // Localize all path keys to ./
87        let mut sources_localized: HashMap<String, String> = HashMap::new();
88        sources.drain().for_each(|(path, source)| {
89            sources_localized.insert(strip_path_prefix(path.as_str()).to_owned(), source);
90        });
91        InMemoryFileProvider { sources: Arc::new(sources_localized) }
92    }
93}
94
95impl FileProvider for InMemoryFileProvider {
96    fn read_file(&self, pb: PathBuf) -> Result<Arc<FileSource>, CompilerError> {
97        let path = pb.to_str().unwrap_or_default();
98        let localized = strip_path_prefix(path);
99        match self.sources.get(localized) {
100            Some(source) => Ok(Arc::new(FileSource {
101                path: path.to_string(),
102                source: Some(source.to_string()),
103                access: Some(time::get_current_time()),
104                dependencies: vec![],
105            })),
106            None => {
107                tracing::error!(target: "core", "FILE READ FAILED: \"{}\"!", path);
108                Err(CompilerError::FileUnpackError(UnpackError::MissingFile(path.to_string())))
109            }
110        }
111    }
112
113    fn transform_paths(&self, sources: &[String]) -> Result<Vec<PathBuf>, CompilerError> {
114        let mut paths = vec![];
115
116        tracing::debug!(target: "core", "InMemoryFileProvider::transform_paths - sources: {:?}", sources);
117
118        for f in sources {
119            // If the file has .huff extension, use the path
120            // Use to_str() for proper string comparison
121            let path = Path::new(f);
122            let has_huff_ext = path.extension().and_then(|ext| ext.to_str()).map(|ext| ext == "huff").unwrap_or(false);
123
124            eprintln!("Checking file: '{}', extension: {:?}, has_huff_ext: {}", f, path.extension(), has_huff_ext);
125            tracing::debug!(target: "core", "Checking file: {}, has_huff_ext: {}", f, has_huff_ext);
126
127            if has_huff_ext {
128                let localized = strip_path_prefix(f);
129                eprintln!("Adding path: '{}'", localized);
130                tracing::debug!(target: "core", "Adding path: {}", localized);
131                paths.push(Path::new(&localized).to_path_buf())
132            }
133        }
134
135        tracing::debug!(target: "core", "InMemoryFileProvider::transform_paths - result: {:?}", paths);
136
137        Ok(paths)
138    }
139}
140
141fn strip_path_prefix(path: &str) -> &str {
142    path.strip_prefix("./").unwrap_or(path)
143}