1use super::check::{Checker, TypeInfo};
13use super::objects::{PackageKey, TCObjects};
14use go_parser::ast;
15use go_parser::{AstObjects, ErrorList, FileSet, Map, Parser, Pos};
16use std::io;
17use std::path::{Path, PathBuf};
18
19pub struct TraceConfig {
20 pub trace_parser: bool,
22 pub trace_checker: bool,
24}
25
26pub trait SourceRead {
27 fn working_dir(&self) -> &Path;
28
29 fn base_dir(&self) -> Option<&Path>;
30
31 fn read_file(&self, path: &Path) -> io::Result<String>;
32
33 fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>>;
34
35 fn is_file(&self, path: &Path) -> bool;
36
37 fn is_dir(&self, path: &Path) -> bool;
38
39 fn canonicalize_import(&self, key: &ImportKey) -> io::Result<(PathBuf, String)>;
40}
41
42#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
49pub struct ImportKey {
50 pub path: String,
51 pub dir: String, }
53
54impl ImportKey {
55 pub fn new(path: &str, dir: &str) -> ImportKey {
56 ImportKey {
57 path: path.to_string(),
58 dir: dir.to_string(),
59 }
60 }
61}
62
63pub struct Importer<'a, S: SourceRead> {
64 trace_config: &'a TraceConfig,
65 reader: &'a S,
66 fset: &'a mut FileSet,
67 pkgs: &'a mut Map<String, PackageKey>,
68 all_results: &'a mut Map<PackageKey, TypeInfo>,
69 ast_objs: &'a mut AstObjects,
70 tc_objs: &'a mut TCObjects,
71 errors: &'a ErrorList,
72 pos: Pos,
73}
74
75impl<'a, S: SourceRead> Importer<'a, S> {
76 pub fn new(
77 config: &'a TraceConfig,
78 reader: &'a S,
79 fset: &'a mut FileSet,
80 pkgs: &'a mut Map<String, PackageKey>,
81 all_results: &'a mut Map<PackageKey, TypeInfo>,
82 ast_objs: &'a mut AstObjects,
83 tc_objs: &'a mut TCObjects,
84 errors: &'a ErrorList,
85 pos: Pos,
86 ) -> Importer<'a, S> {
87 Importer {
88 trace_config: config,
89 reader: reader,
90 fset: fset,
91 pkgs: pkgs,
92 all_results: all_results,
93 ast_objs: ast_objs,
94 tc_objs: tc_objs,
95 errors: errors,
96 pos: pos,
97 }
98 }
99
100 pub fn import(&mut self, key: &'a ImportKey) -> Result<PackageKey, ()> {
101 if key.path == "unsafe" {
102 return Ok(*self.tc_objs.universe().unsafe_pkg());
103 }
104
105 match self.reader.canonicalize_import(key) {
106 Ok((path, import_path)) => match self.pkgs.get(&import_path) {
107 Some(key) => Ok(*key),
108 None => {
109 let pkg = self.tc_objs.new_package(import_path.clone());
110 self.pkgs.insert(import_path, pkg);
111 let files = self.parse_path(&path)?;
112 Checker::new(
113 self.tc_objs,
114 self.ast_objs,
115 self.fset,
116 self.errors,
117 self.pkgs,
118 self.all_results,
119 pkg,
120 self.trace_config,
121 self.reader,
122 )
123 .check(files)
124 }
125 },
126 Err(e) => self.error(format!("canonicalize import error: {}", e)),
127 }
128 }
129
130 fn parse_path(&mut self, path: &Path) -> Result<Vec<ast::File>, ()> {
131 match read_content(path, self.reader) {
132 Ok(contents) => {
133 if contents.len() == 0 {
134 self.error(format!("no source file found in dir: {}", path.display()))
135 } else {
136 let mut afiles = vec![];
137 for (full_name, content) in contents.into_iter() {
138 let mut pfile = self.fset.add_file(
139 full_name,
140 Some(self.fset.base()),
141 content.chars().count(),
142 );
143 let afile = Parser::new(
144 self.ast_objs,
145 &mut pfile,
146 self.errors,
147 &content,
148 self.trace_config.trace_parser,
149 )
150 .parse_file();
151 if afile.is_none() {
152 return Err(());
155 } else {
156 afiles.push(afile.unwrap());
157 }
158 }
159 Ok(afiles)
160 }
161 }
162 Err(e) => self.error(format!(
163 "failed to read from path: {}, {}",
164 path.display(),
165 e
166 )),
167 }
168 }
169
170 fn error<T>(&self, err: String) -> Result<T, ()> {
171 self.errors
172 .add(self.fset.position(self.pos), err, false, false);
173 Err(())
174 }
175}
176
177fn read_content(p: &Path, reader: &dyn SourceRead) -> io::Result<Vec<(String, String)>> {
178 let working_dir = reader.working_dir().canonicalize().ok();
179 let mut result = vec![];
180 let mut read = |path: PathBuf| -> io::Result<()> {
181 if let Some(ext) = path.extension() {
182 if ext == "gos" || ext == "go" || ext == "src" {
183 if let Some(fs) = path.file_stem() {
184 let s = fs.to_str();
185 if s.is_some() && !s.unwrap().ends_with("_test") {
186 let p = path.as_path();
187 let content = reader.read_file(p)?;
188 let full_name = match &working_dir {
190 Some(wd) => p.strip_prefix(wd).unwrap_or(p),
191 None => p,
192 }
193 .to_string_lossy()
194 .to_string();
195 result.push((full_name, content))
196 }
197 }
198 }
199 }
200 Ok(())
201 };
202
203 if reader.is_dir(p) {
204 let mut paths = reader.read_dir(p)?;
205 paths.sort_by(|a, b| a.as_os_str().cmp(b.as_os_str()));
206 for p in paths.into_iter() {
207 read(p)?;
208 }
209 } else if reader.is_file(p) {
210 read(p.to_path_buf())?;
211 }
212 if result.len() == 0 {
213 return Err(io::Error::new(io::ErrorKind::Other, "no file/dir found"));
214 }
215 Ok(result)
216}