Skip to content

Commit 0abffb2

Browse files
authored
Merge pull request #231 from seddonym/path-components-simplify
Path components simplify
2 parents a1a0527 + 9d5b610 commit 0abffb2

4 files changed

Lines changed: 38 additions & 34 deletions

File tree

rust/src/filesystem.rs

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use pyo3::exceptions::{PyFileNotFoundError, PyUnicodeDecodeError};
22
use pyo3::prelude::*;
33
use regex::Regex;
44
use std::collections::HashMap;
5+
use std::ffi::OsStr;
56
use std::fs;
67
use std::path::{Path, PathBuf};
78
use unindent::unindent;
@@ -199,33 +200,21 @@ impl FileSystem for FakeBasicFileSystem {
199200
}
200201

201202
fn split(&self, file_name: &str) -> (String, String) {
202-
let components: Vec<&str> = file_name.split('/').collect();
203-
204-
if components.is_empty() {
205-
return ("".to_string(), "".to_string());
206-
}
207-
208-
let tail = components.last().unwrap_or(&""); // Last component, or empty if components is empty (shouldn't happen from split)
209-
210-
let head_components = &components[..components.len() - 1]; // All components except the last
211-
212-
let head = if head_components.is_empty() {
213-
// Case for single component paths like "filename.txt" or empty string ""
214-
"".to_string()
215-
} else if file_name.starts_with('/')
216-
&& head_components.len() == 1
217-
&& head_components[0].is_empty()
218-
{
219-
// Special handling for paths starting with '/', e.g., "/" or "/filename.txt"
220-
// If components were ["", ""], head_components is [""] -> should be "/"
221-
// If components were ["", "file.txt"], head_components is [""] -> should be "/"
222-
"/".to_string()
203+
let path;
204+
let head;
205+
let tail;
206+
if let Some(file_name_without_trailing_slash) = file_name.strip_suffix("/") {
207+
head = Path::new(file_name_without_trailing_slash);
208+
tail = OsStr::new("");
223209
} else {
224-
// Default joining for multiple components
225-
head_components.join("/")
226-
};
227-
228-
(head, tail.to_string())
210+
path = Path::new(file_name);
211+
head = path.parent().unwrap_or(Path::new(""));
212+
tail = path.file_name().unwrap_or(OsStr::new(""));
213+
}
214+
(
215+
head.to_str().unwrap().to_string(),
216+
tail.to_str().unwrap().to_string(),
217+
)
229218
}
230219

231220
/// Checks if a file or directory exists within the file system.

rust/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
pub mod errors;
22
pub mod exceptions;
3+
mod filesystem;
34
pub mod graph;
45
pub mod import_parsing;
5-
pub mod module_expressions;
6-
mod filesystem;
76
mod import_scanning;
7+
pub mod module_expressions;
88
mod module_finding;
99

1010
use crate::errors::{GrimpError, GrimpResult};
1111
use crate::exceptions::{InvalidModuleExpression, ModuleNotPresent, NoSuchContainer, ParseError};
12+
use crate::filesystem::{PyFakeBasicFileSystem, PyRealBasicFileSystem};
1213
use crate::graph::higher_order_queries::Level;
1314
use crate::graph::{Graph, Module, ModuleIterator, ModuleTokenIterator};
1415
use crate::import_scanning::ImportScanner;
@@ -22,7 +23,6 @@ use pyo3::types::{IntoPyDict, PyDict, PyFrozenSet, PyList, PySet, PyString, PyTu
2223
use rayon::prelude::*;
2324
use rustc_hash::FxHashSet;
2425
use std::collections::HashSet;
25-
use crate::filesystem::{PyRealBasicFileSystem,PyFakeBasicFileSystem};
2626

2727
#[pymodule]
2828
fn _rustgrimp(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {

tests/adaptors/filesystem.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from typing import Any, Dict, Generator, List, Optional, Tuple
22

3-
43
import yaml
54

65
from grimp.application.ports.filesystem import AbstractFileSystem, BasicFileSystem
@@ -99,8 +98,13 @@ def join(self, *components: str) -> str:
9998
return self.sep.join(c.rstrip(self.sep) for c in components)
10099

101100
def split(self, file_name: str) -> Tuple[str, str]:
102-
components = file_name.split("/")
103-
return ("/".join(components[:-1]), components[-1])
101+
components = file_name.split(self.sep)
102+
if len(components) == 2:
103+
# Handle case where file is child of the root, i.e. /some-file.txt.
104+
# In this case, to conform with the interface we ensure the leading slash
105+
# is included in the returned head.
106+
components.insert(0, "")
107+
return (self.sep.join(components[:-1]), components[-1])
104108

105109
def _parse_contents(self, raw_contents: Optional[str]):
106110
"""

tests/unit/adaptors/test_filesystem.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,20 @@ def test_join(self, path):
1818
file_system = self.file_system_cls()
1919
assert "/path/to/mypackage/file.py" == file_system.join(path, "mypackage", "file.py")
2020

21-
def test_split(self):
21+
@pytest.mark.parametrize(
22+
"path, expected",
23+
[
24+
("", ("", "")),
25+
("/path", ("/", "path")),
26+
("some-file", ("", "some-file")),
27+
("/path/to/mypackage/", ("/path/to/mypackage", "")),
28+
("/path/to/mypackage/some-file", ("/path/to/mypackage", "some-file")),
29+
("/path/to/mypackage/some-file.py", ("/path/to/mypackage", "some-file.py")),
30+
],
31+
)
32+
def test_split(self, path, expected):
2233
file_system = self.file_system_cls()
23-
assert ("/path/to/mypackage", "file.py") == file_system.split("/path/to/mypackage/file.py")
34+
assert file_system.split(path) == expected
2435

2536
@pytest.mark.parametrize(
2637
"file_name, expected",

0 commit comments

Comments
 (0)