Skip to content

Commit 9976781

Browse files
committed
Define ImportScanner in Rust
1 parent 74e2a78 commit 9976781

7 files changed

Lines changed: 566 additions & 300 deletions

File tree

rust/src/filesystem.rs

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,36 @@
1-
use pyo3::exceptions::{PyFileNotFoundError,PyRuntimeError};
1+
use pyo3::exceptions::{PyFileNotFoundError, PyRuntimeError};
22
use pyo3::prelude::*;
33
use std::collections::HashMap;
44
type FileSystemContents = HashMap<String, String>;
5-
use unindent::unindent;
6-
use std::path::{Path,PathBuf};
75
use std::fs;
6+
use std::path::{Path, PathBuf};
7+
use unindent::unindent;
8+
9+
pub trait FileSystem: Send + Sync {
10+
fn sep(&self) -> String;
11+
12+
fn join(&self, components: Vec<String>) -> String;
13+
14+
fn split(&self, file_name: &str) -> (String, String);
815

9-
#[pyclass]
16+
fn exists(&self, file_name: &str) -> bool;
17+
18+
fn read(&self, file_name: &str) -> PyResult<String>;
19+
}
20+
21+
#[derive(Clone)]
1022
pub struct RealBasicFileSystem {}
1123

12-
#[pymethods]
13-
impl RealBasicFileSystem {
14-
#[new]
15-
fn new() -> Self {
16-
RealBasicFileSystem {}
17-
}
24+
#[pyclass(name = "RealBasicFileSystem")]
25+
pub struct PyRealBasicFileSystem {
26+
pub inner: RealBasicFileSystem,
27+
}
1828

19-
#[getter]
29+
impl FileSystem for RealBasicFileSystem {
2030
fn sep(&self) -> String {
2131
std::path::MAIN_SEPARATOR.to_string()
2232
}
2333

24-
#[pyo3(signature = (*components))]
2534
fn join(&self, components: Vec<String>) -> String {
2635
let mut path = PathBuf::new();
2736
for component in components {
@@ -45,30 +54,65 @@ impl RealBasicFileSystem {
4554
None => PathBuf::new(), // If there's no parent (e.g., just a filename), return empty
4655
};
4756

48-
(head.to_str().unwrap().to_string(), tail.to_str().unwrap().to_string())
57+
(
58+
head.to_str().unwrap().to_string(),
59+
tail.to_str().unwrap().to_string(),
60+
)
4961
}
50-
62+
5163
fn exists(&self, file_name: &str) -> bool {
5264
Path::new(file_name).is_file()
5365
}
5466

5567
fn read(&self, file_name: &str) -> PyResult<String> {
5668
// TODO: is this good enough for handling non-UTF8 encodings?
57-
fs::read_to_string(file_name).map_err(
58-
|_| PyRuntimeError::new_err(format!("Could not read {}", file_name))
59-
)
69+
fs::read_to_string(file_name)
70+
.map_err(|_| PyRuntimeError::new_err(format!("Could not read {}", file_name)))
6071
}
6172
}
6273

63-
#[pyclass]
74+
#[pymethods]
75+
impl PyRealBasicFileSystem {
76+
#[new]
77+
fn new() -> Self {
78+
PyRealBasicFileSystem {
79+
inner: RealBasicFileSystem {},
80+
}
81+
}
82+
83+
#[getter]
84+
fn sep(&self) -> String {
85+
self.inner.sep()
86+
}
87+
88+
#[pyo3(signature = (*components))]
89+
fn join(&self, components: Vec<String>) -> String {
90+
self.inner.join(components)
91+
}
92+
93+
fn split(&self, file_name: &str) -> (String, String) {
94+
self.inner.split(file_name)
95+
}
96+
97+
fn exists(&self, file_name: &str) -> bool {
98+
self.inner.exists(file_name)
99+
}
100+
101+
fn read(&self, file_name: &str) -> PyResult<String> {
102+
self.inner.read(file_name)
103+
}
104+
}
105+
#[derive(Clone)]
64106
pub struct FakeBasicFileSystem {
65-
contents: FileSystemContents,
107+
contents: Box<FileSystemContents>,
108+
}
109+
110+
#[pyclass(name = "FakeBasicFileSystem")]
111+
pub struct PyFakeBasicFileSystem {
112+
pub inner: FakeBasicFileSystem,
66113
}
67114

68-
#[pymethods]
69115
impl FakeBasicFileSystem {
70-
#[pyo3(signature = (contents=None, content_map=None))]
71-
#[new]
72116
fn new(contents: Option<&str>, content_map: Option<HashMap<String, String>>) -> PyResult<Self> {
73117
let mut parsed_contents = match contents {
74118
Some(contents) => parse_indented_fs_string(contents),
@@ -82,16 +126,16 @@ impl FakeBasicFileSystem {
82126
parsed_contents.extend(unindented_map);
83127
};
84128
Ok(FakeBasicFileSystem {
85-
contents: parsed_contents,
129+
contents: Box::new(parsed_contents),
86130
})
87131
}
132+
}
88133

89-
#[getter]
134+
impl FileSystem for FakeBasicFileSystem {
90135
fn sep(&self) -> String {
91136
"/".to_string()
92137
}
93138

94-
#[pyo3(signature = (*components))]
95139
fn join(&self, components: Vec<String>) -> String {
96140
let sep = self.sep();
97141
components
@@ -131,7 +175,6 @@ impl FakeBasicFileSystem {
131175
(head, tail.to_string())
132176
}
133177

134-
/// Checks if a file or directory exists within the file system.
135178
fn exists(&self, file_name: &str) -> bool {
136179
self.contents.contains_key(file_name)
137180
}
@@ -144,6 +187,40 @@ impl FakeBasicFileSystem {
144187
}
145188
}
146189

190+
#[pymethods]
191+
impl PyFakeBasicFileSystem {
192+
#[pyo3(signature = (contents=None, content_map=None))]
193+
#[new]
194+
fn new(contents: Option<&str>, content_map: Option<HashMap<String, String>>) -> PyResult<Self> {
195+
Ok(PyFakeBasicFileSystem {
196+
inner: FakeBasicFileSystem::new(contents, content_map)?,
197+
})
198+
}
199+
200+
#[getter]
201+
fn sep(&self) -> String {
202+
self.inner.sep()
203+
}
204+
205+
#[pyo3(signature = (*components))]
206+
fn join(&self, components: Vec<String>) -> String {
207+
self.inner.join(components)
208+
}
209+
210+
fn split(&self, file_name: &str) -> (String, String) {
211+
self.inner.split(file_name)
212+
}
213+
214+
/// Checks if a file or directory exists within the file system.
215+
fn exists(&self, file_name: &str) -> bool {
216+
self.inner.exists(file_name)
217+
}
218+
219+
fn read(&self, file_name: &str) -> PyResult<String> {
220+
self.inner.read(file_name)
221+
}
222+
}
223+
147224
/// Parses an indented string representing a file system structure
148225
/// into a HashMap where keys are full file paths.
149226
///

0 commit comments

Comments
 (0)