forked from RustPython/RustPython
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfspath.rs
More file actions
155 lines (143 loc) · 4.68 KB
/
Copy pathfspath.rs
File metadata and controls
155 lines (143 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use crate::{
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
builtins::{PyBytes, PyBytesRef, PyStrRef},
convert::{IntoPyException, ToPyObject},
function::PyStr,
protocol::PyBuffer,
};
use alloc::borrow::Cow;
use std::{ffi::OsStr, path::PathBuf};
/// Helper to implement os.fspath()
#[derive(Clone)]
pub enum FsPath {
Str(PyStrRef),
Bytes(PyBytesRef),
}
impl FsPath {
pub fn try_from_path_like(
obj: PyObjectRef,
check_for_nul: bool,
vm: &VirtualMachine,
) -> PyResult<Self> {
Self::try_from(
obj,
check_for_nul,
"expected str, bytes or os.PathLike object",
vm,
)
}
// PyOS_FSPath
pub fn try_from(
obj: PyObjectRef,
check_for_nul: bool,
msg: &'static str,
vm: &VirtualMachine,
) -> PyResult<Self> {
let check_nul = |b: &[u8]| {
if !check_for_nul || memchr::memchr(b'\0', b).is_none() {
Ok(())
} else {
Err(crate::exceptions::cstring_error(vm))
}
};
let match1 = |obj: PyObjectRef| {
let pathlike = match_class!(match obj {
s @ PyStr => {
check_nul(s.as_bytes())?;
Self::Str(s)
}
b @ PyBytes => {
check_nul(&b)?;
Self::Bytes(b)
}
obj => return Ok(Err(obj)),
});
Ok(Ok(pathlike))
};
let obj = match match1(obj)? {
Ok(pathlike) => return Ok(pathlike),
Err(obj) => obj,
};
let not_pathlike_error = || format!("{msg}, not {}", obj.class().name());
let method = vm.get_method_or_type_error(
obj.clone(),
identifier!(vm, __fspath__),
not_pathlike_error,
)?;
// If __fspath__ is explicitly set to None, treat it as if it doesn't have __fspath__
if vm.is_none(&method) {
return Err(vm.new_type_error(not_pathlike_error()));
}
let result = method.call((), vm)?;
match1(result)?.map_err(|result| {
vm.new_type_error(format!(
"expected {}.__fspath__() to return str or bytes, not {}",
obj.class().name(),
result.class().name(),
))
})
}
pub fn as_os_str(&self, vm: &VirtualMachine) -> PyResult<Cow<'_, OsStr>> {
// TODO: FS encodings
match self {
Self::Str(s) => vm.fsencode(s),
Self::Bytes(b) => Self::bytes_as_os_str(b.as_bytes(), vm).map(Cow::Borrowed),
}
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
// TODO: FS encodings
match self {
Self::Str(s) => s.as_bytes(),
Self::Bytes(b) => b.as_bytes(),
}
}
#[must_use]
pub fn to_string_lossy(&self) -> Cow<'_, str> {
match self {
Self::Str(s) => s.to_string_lossy(),
Self::Bytes(s) => String::from_utf8_lossy(s),
}
}
pub fn to_path_buf(&self, vm: &VirtualMachine) -> PyResult<PathBuf> {
let path = match self {
Self::Str(s) => PathBuf::from(vm.fsencode(s)?.as_ref() as &OsStr),
Self::Bytes(b) => PathBuf::from(Self::bytes_as_os_str(b, vm)?),
};
Ok(path)
}
pub fn to_cstring(&self, vm: &VirtualMachine) -> PyResult<alloc::ffi::CString> {
alloc::ffi::CString::new(self.as_bytes()).map_err(|e| e.into_pyexception(vm))
}
#[cfg(windows)]
pub fn to_wide_cstring(&self, vm: &VirtualMachine) -> PyResult<widestring::WideCString> {
widestring::WideCString::from_os_str(self.as_os_str(vm)?)
.map_err(|err| err.into_pyexception(vm))
}
pub fn bytes_as_os_str<'a>(b: &'a [u8], vm: &VirtualMachine) -> PyResult<&'a std::ffi::OsStr> {
rustpython_host_env::os::bytes_as_os_str(b)
.map_err(|_| vm.new_unicode_decode_error("can't decode path for utf-8"))
}
}
impl ToPyObject for FsPath {
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
match self {
Self::Str(s) => s.into(),
Self::Bytes(b) => b.into(),
}
}
}
impl TryFromObject for FsPath {
// PyUnicode_FSDecoder in CPython
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let obj = match obj.try_to_value::<PyBuffer>(vm) {
Ok(buffer) => {
let mut bytes = vec![];
buffer.append_to(&mut bytes);
vm.ctx.new_bytes(bytes).into()
}
Err(_) => obj,
};
Self::try_from_path_like(obj, true, vm)
}
}