go_engine/
source.rs

1// Copyright 2022 The Goscript Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5use crate::engine::{Config, Engine, ImportKey, SourceRead};
6use crate::vfs::VirtualFs;
7use crate::ErrorList;
8use go_parser::Map;
9use std::io;
10use std::path::{Path, PathBuf};
11use std::rc::Rc;
12
13pub fn run(
14    config: Config,
15    source: &SourceReader,
16    path: &Path,
17    panic_handler: Option<Rc<dyn Fn(String, String)>>,
18) -> Result<(), ErrorList> {
19    let engine = Engine::new();
20    #[cfg(feature = "go_std")]
21    engine.set_std_io(config.std_in, config.std_out, config.std_err);
22    engine.run_source(
23        config.trace_parser,
24        config.trace_checker,
25        source,
26        path,
27        panic_handler,
28    )
29}
30
31pub struct SourceReader {
32    /// base directory for non-local imports(library files)
33    base_dir: Option<PathBuf>,
34    /// working directory
35    working_dir: PathBuf,
36    /// The virtual file system from which to read files.
37    vfs: Box<dyn VirtualFs>,
38}
39
40impl SourceReader {
41    pub fn new(
42        base_dir: Option<PathBuf>,
43        working_dir: PathBuf,
44        vfs: Box<dyn VirtualFs>,
45    ) -> SourceReader {
46        SourceReader {
47            base_dir,
48            working_dir,
49            vfs,
50        }
51    }
52
53    /// Create a SourceReader that reads from local file system.
54    #[cfg(feature = "read_fs")]
55    pub fn local_fs(base_dir: PathBuf, working_dir: PathBuf) -> SourceReader {
56        SourceReader::new(Some(base_dir), working_dir, Box::new(crate::VfsFs {}))
57    }
58
59    /// Create a SourceReader that reads from a zip file and a string.
60    #[cfg(feature = "read_fs")]
61    pub fn fs_lib_and_string(
62        base_dir: PathBuf,
63        source: std::borrow::Cow<'static, str>,
64    ) -> (SourceReader, PathBuf) {
65        let temp_file_name = "temp_file.gos";
66        let vfs_map_name = "vfs_map";
67        let vfs_fs_name = "vfs_fs";
68        (
69            SourceReader::new(
70                Some(Path::new(vfs_fs_name).join(base_dir)),
71                PathBuf::from(format!("{}/", vfs_map_name)),
72                Box::new(crate::CompoundFs::new(Map::from([
73                    (
74                        vfs_fs_name.to_owned(),
75                        Box::new(crate::VfsFs {}) as Box<dyn VirtualFs>,
76                    ),
77                    (
78                        vfs_map_name.to_owned(),
79                        Box::new(crate::VfsMap::new(Map::from([(
80                            PathBuf::from(temp_file_name),
81                            source,
82                        )]))),
83                    ),
84                ]))),
85            ),
86            PathBuf::from(format!("./{}", temp_file_name)),
87        )
88    }
89
90    /// Creates a SourceReader that reads from a zip archive and a string.
91    /// Returns the SourceReader and the path of the virtual file that contains the string.
92    #[cfg(feature = "read_zip")]
93    pub fn zip_lib_and_string(
94        archive: std::borrow::Cow<'static, [u8]>,
95        base_dir: PathBuf,
96        source: std::borrow::Cow<'static, str>,
97    ) -> (SourceReader, PathBuf) {
98        let temp_file_name = "temp_file.gos";
99        let vfs_map_name = "vfs_map";
100        let vfs_zip_name = "vfs_zip";
101        (
102            SourceReader::new(
103                Some(Path::new(vfs_zip_name).join(base_dir)),
104                PathBuf::from(format!("{}/", vfs_map_name)),
105                Box::new(crate::CompoundFs::new(Map::from([
106                    (
107                        vfs_zip_name.to_owned(),
108                        Box::new(crate::VfsZip::new(archive).unwrap()) as Box<dyn VirtualFs>,
109                    ),
110                    (
111                        vfs_map_name.to_owned(),
112                        Box::new(crate::VfsMap::new(Map::from([(
113                            PathBuf::from(temp_file_name),
114                            source,
115                        )]))),
116                    ),
117                ]))),
118            ),
119            PathBuf::from(format!("./{}", temp_file_name)),
120        )
121    }
122
123    /// Creates a SourceReader that reads from a zip archive and local file system.
124    /// Can be used when you want to read library files from a zip archive and user's
125    /// source code from the local file system.
126    #[cfg(feature = "read_zip")]
127    pub fn zip_lib_and_local_fs(
128        archive: std::borrow::Cow<'static, [u8]>,
129        base_dir: PathBuf,
130        working_dir: PathBuf,
131    ) -> SourceReader {
132        let vfs_fs_name = "local_fs";
133        let vfs_zip_name = "vfs_zip";
134
135        SourceReader::new(
136            Some(Path::new(vfs_zip_name).join(base_dir)),
137            Path::new(&vfs_fs_name).join(working_dir),
138            Box::new(crate::CompoundFs::new(Map::from([
139                (
140                    vfs_fs_name.to_owned(),
141                    Box::new(crate::VfsFs {}) as Box<dyn VirtualFs>,
142                ),
143                (
144                    vfs_zip_name.to_owned(),
145                    Box::new(crate::VfsZip::new(archive).unwrap()) as Box<dyn VirtualFs>,
146                ),
147            ]))),
148        )
149    }
150}
151
152impl SourceRead for SourceReader {
153    fn working_dir(&self) -> &Path {
154        &self.working_dir
155    }
156
157    fn base_dir(&self) -> Option<&Path> {
158        self.base_dir.as_ref().map(|x| x.as_path())
159    }
160
161    fn read_file(&self, path: &Path) -> io::Result<String> {
162        self.vfs.read_file(path)
163    }
164
165    fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>> {
166        self.vfs.read_dir(path)
167    }
168
169    fn is_file(&self, path: &Path) -> bool {
170        self.vfs.is_file(path)
171    }
172
173    fn is_dir(&self, path: &Path) -> bool {
174        self.vfs.is_dir(path)
175    }
176
177    fn canonicalize_import(&self, key: &ImportKey) -> io::Result<(PathBuf, String)> {
178        let mut import_path = key.path.clone();
179        let path = if self.vfs.is_local(&key.path) {
180            let mut wd = self.working_dir().to_owned();
181            wd.push(self.vfs.strip_prefix(Path::new(&key.dir)));
182            wd.push(&key.path);
183            if let Some(base) = &self.base_dir() {
184                if let Ok(rel) = wd.as_path().strip_prefix(base) {
185                    import_path = rel.to_string_lossy().to_string()
186                }
187            }
188            wd
189        } else {
190            if let Some(base) = &self.base_dir() {
191                let mut p = PathBuf::new();
192                p.push(base);
193                p.push(&key.path);
194                p
195            } else {
196                return Err(io::Error::new(
197                    io::ErrorKind::Other,
198                    format!("base dir required for path: {}", key.path),
199                ));
200            }
201        };
202        self.vfs.canonicalize_path(&path).map(|p| (p, import_path))
203    }
204}