1#![allow(dead_code)]
2use super::check::check::{Checker, TypeInfo};
3use super::objects::{PackageKey, TCObjects};
4use goscript_parser::ast;
5use goscript_parser::errors::{ErrorList, FilePosErrors};
6use goscript_parser::objects::Objects as AstObjects;
7use goscript_parser::position;
8use goscript_parser::{FileSet, Parser};
9use std::collections::HashMap;
10use std::env;
11use std::fs;
12use std::io;
13use std::path::{Path, PathBuf};
14
15pub struct Config {
16 pub work_dir: Option<String>,
18 pub base_path: Option<String>,
20 pub trace_parser: bool,
22 pub trace_checker: bool,
24}
25
26impl Config {
27 fn get_working_dir(&self) -> io::Result<PathBuf> {
28 if let Some(wd) = &self.work_dir {
29 let mut buf = PathBuf::new();
30 buf.push(wd);
31 Ok(buf)
32 } else {
33 env::current_dir()
34 }
35 }
36}
37
38#[derive(PartialEq, Eq, Hash, Debug)]
45pub struct ImportKey {
46 pub path: String,
47 pub dir: String, }
49
50impl ImportKey {
51 pub fn new(path: &str, dir: &str) -> ImportKey {
52 ImportKey {
53 path: path.to_string(),
54 dir: dir.to_string(),
55 }
56 }
57}
58
59pub struct Importer<'a> {
60 config: &'a Config,
61 fset: &'a mut FileSet,
62 pkgs: &'a mut HashMap<String, PackageKey>,
63 all_results: &'a mut HashMap<PackageKey, TypeInfo>,
64 ast_objs: &'a mut AstObjects,
65 tc_objs: &'a mut TCObjects,
66 errors: &'a ErrorList,
67 pos: position::Pos,
68}
69
70impl<'a> Importer<'a> {
71 pub fn new(
72 config: &'a Config,
73 fset: &'a mut FileSet,
74 pkgs: &'a mut HashMap<String, PackageKey>,
75 all_results: &'a mut HashMap<PackageKey, TypeInfo>,
76 ast_objs: &'a mut AstObjects,
77 tc_objs: &'a mut TCObjects,
78 errors: &'a ErrorList,
79 pos: position::Pos,
80 ) -> Importer<'a> {
81 Importer {
82 config: config,
83 fset: fset,
84 pkgs: pkgs,
85 all_results: all_results,
86 ast_objs: ast_objs,
87 tc_objs: tc_objs,
88 errors: errors,
89 pos: pos,
90 }
91 }
92
93 pub fn import(&mut self, key: &'a ImportKey) -> Result<PackageKey, ()> {
94 if key.path == "unsafe" {
95 return Ok(*self.tc_objs.universe().unsafe_pkg());
96 }
97 let pb = self.validate_path(key)?;
98 let path = pb.0.as_path();
99 let import_path = pb.1;
100 let pkg = self.tc_objs.new_package(import_path.clone());
101 self.pkgs.insert(import_path, pkg);
102 let files = self.parse_dir(path)?;
103 Checker::new(
104 self.tc_objs,
105 self.ast_objs,
106 self.fset,
107 self.errors,
108 self.pkgs,
109 self.all_results,
110 pkg,
111 self.config,
112 )
113 .check(files)
114 }
115
116 fn validate_path(&mut self, key: &'a ImportKey) -> Result<(PathBuf, String), ()> {
117 let mut import_path = key.path.clone();
118 let path = if is_local(&key.path) {
119 let working_dir = self.config.get_working_dir();
120 if working_dir.is_err() {
121 self.error(format!("failed to get working dir for: {}", key.path));
122 return Err(());
123 }
124 let mut wd = working_dir.unwrap();
125 wd.push(&key.dir);
126 wd.push(&key.path);
127 if let Some(base) = &self.config.base_path {
128 if let Ok(rel) = wd.as_path().strip_prefix(base) {
129 import_path = rel.to_string_lossy().to_string()
130 }
131 }
132 wd
133 } else {
134 if let Some(base) = &self.config.base_path {
135 let mut p = PathBuf::new();
136 p.push(base);
137 p.push(&key.path);
138 p
139 } else {
140 self.error(format!("base dir required for path: {}", key.path));
141 return Err(());
142 }
143 };
144 if !path.exists() {
145 self.error(format!("failed to locate path: {}", key.path));
146 return Err(());
147 }
148 match path.canonicalize() {
149 Ok(p) => Ok((p, import_path)),
150 Err(_) => {
151 self.error(format!("failed to canonicalize path: {}", key.path));
152 return Err(());
153 }
154 }
155 }
156
157 fn parse_dir(&mut self, path: &Path) -> Result<Vec<ast::File>, ()> {
158 let working_dir = self
159 .config
160 .get_working_dir()
161 .ok()
162 .map(|x| x.canonicalize().ok())
163 .flatten();
164 match read_content(path) {
165 Ok(contents) => {
166 if contents.len() == 0 {
167 self.error(format!("no source file found in dir: {}", path.display()));
168 Err(())
169 } else {
170 let mut afiles = vec![];
171 for (path_buf, content) in contents.into_iter() {
172 let p = path_buf.as_path();
174 let full_name = match &working_dir {
175 Some(wd) => p.strip_prefix(wd).unwrap_or(p),
176 None => p,
177 }
178 .to_string_lossy()
179 .to_string();
180 let mut pfile = self.fset.add_file(
181 full_name,
182 Some(self.fset.base()),
183 content.chars().count(),
184 );
185 let afile = Parser::new(
186 self.ast_objs,
187 &mut pfile,
188 self.errors,
189 &content,
190 self.config.trace_parser,
191 )
192 .parse_file();
193 if afile.is_none() {
194 return Err(());
197 } else {
198 afiles.push(afile.unwrap());
199 }
200 }
201 Ok(afiles)
202 }
203 }
204 Err(_) => {
205 self.error(format!("failed to read dir: {}", path.display()));
206 Err(())
207 }
208 }
209 }
210
211 fn error(&self, err: String) {
212 let pos_file = self.fset.file(self.pos).unwrap();
213 FilePosErrors::new(pos_file, self.errors).add(self.pos, err, false);
214 }
215}
216
217fn read_content(p: &Path) -> io::Result<Vec<(PathBuf, String)>> {
218 let mut result = vec![];
219 let mut read = |path: PathBuf| -> io::Result<()> {
220 if let Some(ext) = path.extension() {
221 if ext == "gos" || ext == "go" || ext == "src" {
222 if let Some(fs) = path.file_stem() {
223 let s = fs.to_str();
224 if s.is_some() && !s.unwrap().ends_with("_test") {
225 let content = fs::read_to_string(path.as_path())?;
226 result.push((path, content))
227 }
228 }
229 }
230 }
231 Ok(())
232 };
233
234 if p.is_dir() {
235 let mut paths = vec![];
236 for entry in fs::read_dir(p)? {
237 let entry = entry?;
238 let path = entry.path();
239 if !path.is_dir() {
240 paths.push(path);
241 }
242 }
243 paths.sort_by(|a, b| a.as_os_str().cmp(b.as_os_str()));
244 for p in paths.into_iter() {
245 read(p)?;
246 }
247 } else if p.is_file() {
248 read(p.to_path_buf())?;
249 }
250 if result.len() == 0 {
251 return Err(io::Error::new(io::ErrorKind::Other, "no file/dir found"));
252 }
253 Ok(result)
254}
255
256fn is_local(path: &str) -> bool {
257 path == "." || path == ".." || path.starts_with("./") || path.starts_with("../")
258}