Skip to content

Commit 77fb733

Browse files
committed
std: xous: add support for path
Add support for the Xous-specific path implementation. Signed-off-by: Sean Cross <sean@xobs.io>
1 parent 19264d5 commit 77fb733

File tree

4 files changed

+293
-0
lines changed

4 files changed

+293
-0
lines changed

library/std/src/os/xous/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod ffi;
77
#[stable(feature = "rust1", since = "1.0.0")]
88
pub mod services;
99

10+
pub mod path;
1011
/// A prelude for conveniently writing platform-specific code.
1112
///
1213
/// Includes all extension traits, and some important type definitions.

library/std/src/os/xous/path.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#![stable(feature = "rust1", since = "1.0.0")]
2+
3+
use crate::path;
4+
5+
#[stable(feature = "file_type_ext", since = "1.5.0")]
6+
pub trait PathExt {
7+
/// Returns `true` if this file type is a basis.
8+
///
9+
/// # Examples
10+
///
11+
/// ```no_run
12+
/// use std::fs;
13+
/// use std::os::xous::fs::FileTypeExt;
14+
/// use std::io;
15+
///
16+
/// fn main() -> io::Result<()> {
17+
/// let meta = fs::metadata(":.System")?;
18+
/// let file_type = meta.file_type();
19+
/// assert!(file_type.is_basis());
20+
/// Ok(())
21+
/// }
22+
/// ```
23+
#[stable(feature = "file_type_ext", since = "1.5.0")]
24+
fn is_basis(&self) -> bool;
25+
}
26+
27+
#[stable(feature = "file_type_ext", since = "1.5.0")]
28+
impl PathExt for path::Path {
29+
fn is_basis(&self) -> bool {
30+
let Some(as_str) = self.as_os_str().to_str() else {
31+
return false;
32+
};
33+
34+
let Ok(path) = crate::sys::path::split_basis_dict_key(as_str) else {
35+
return false;
36+
};
37+
38+
path.basis.is_some() && path.dict.is_none() && path.key.is_none()
39+
}
40+
}

library/std/src/sys/path/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ cfg_select! {
1616
mod uefi;
1717
pub use uefi::*;
1818
}
19+
target_os = "xous" => {
20+
mod xous;
21+
pub use xous::*;
22+
}
1923
target_os = "cygwin" => {
2024
mod cygwin;
2125
mod windows_prefix;

library/std/src/sys/path/xous.rs

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
use crate::ffi::OsStr;
2+
use crate::io;
3+
use crate::path::{Path, PathBuf, Prefix};
4+
5+
#[inline]
6+
pub fn is_sep_byte(b: u8) -> bool {
7+
b == b'/' || b == b':'
8+
}
9+
10+
#[inline]
11+
pub fn is_verbatim_sep(b: u8) -> bool {
12+
b == b'/'
13+
}
14+
15+
#[inline]
16+
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
17+
None
18+
}
19+
20+
pub const HAS_PREFIXES: bool = false;
21+
pub const MAIN_SEP_STR: &str = "/";
22+
pub const MAIN_SEP: char = '/';
23+
pub const ALLOWED_SEP: &[char] = &['/', ':'];
24+
25+
/// Make a POSIX path absolute without changing its semantics.
26+
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
27+
Ok(path.to_owned())
28+
}
29+
30+
pub(crate) fn is_absolute(path: &Path) -> bool {
31+
path.has_root() && path.prefix().is_some()
32+
}
33+
34+
#[derive(Debug, PartialEq)]
35+
pub enum BasisSplitError {
36+
/// This basis couldn't be split because it's the root element
37+
Root,
38+
/// This basis couldn't be split because it's listing all bases
39+
All,
40+
/// A path component was empty
41+
EmptyComponent,
42+
}
43+
44+
#[derive(Default, Debug, PartialEq)]
45+
pub struct SplitPath<'a> {
46+
pub basis: Option<&'a str>,
47+
pub dict: Option<&'a str>,
48+
pub key: Option<&'a str>,
49+
}
50+
51+
/// Split a path into its constituant Basis, Dict, and Key, if the path is legal.
52+
/// A Basis of `None` indicates the default Basis.
53+
pub fn split_basis_dict_key<'a>(src: &'a str) -> Result<SplitPath<'a>, BasisSplitError> {
54+
let mut current_string = src;
55+
let mut path = SplitPath::default();
56+
57+
// Special case for root
58+
if src == ":" || src == "/" {
59+
return Err(BasisSplitError::Root);
60+
}
61+
// Special case for listing all bases
62+
if src == "::" || src == "//" || src == "/:" || src == ":/" {
63+
return Err(BasisSplitError::All);
64+
}
65+
66+
// If the string is an absolute path, then the first component is a basis
67+
if let Some(src) = src.strip_prefix(ALLOWED_SEP) {
68+
// See if we can split off a second separator which will get us the end
69+
// of the Basis name
70+
if let Some((maybe_basis, remainder)) = src.split_once(ALLOWED_SEP) {
71+
// If the basis is empty, pull it from the default Basis function.
72+
// Otherwise, use the specified basis.
73+
if !maybe_basis.is_empty() {
74+
path.basis = Some(maybe_basis);
75+
}
76+
current_string = remainder;
77+
}
78+
}
79+
80+
// Now that we have a fully-specified Basis, pull out the rest of the string.
81+
if let Some(components) = current_string.rsplit_once(ALLOWED_SEP) {
82+
let (mut dict, key) = components;
83+
if let Some(stripped_dict) = dict.strip_prefix(ALLOWED_SEP) {
84+
dict = stripped_dict;
85+
}
86+
// Don't allow empty dicts
87+
if components.0.is_empty() {
88+
return Err(BasisSplitError::EmptyComponent);
89+
}
90+
for path_component in dict.split(ALLOWED_SEP) {
91+
if path_component.is_empty() {
92+
return Err(BasisSplitError::EmptyComponent);
93+
}
94+
}
95+
96+
path.dict = Some(dict);
97+
// Don't allow empty keys
98+
if !key.is_empty() {
99+
path.key = Some(key);
100+
}
101+
} else if !current_string.is_empty() {
102+
path.dict = Some(current_string)
103+
}
104+
105+
Ok(path)
106+
}
107+
108+
#[cfg(test)]
109+
fn assert_basis_dict_key(test: &str, basis: Option<&str>, dict: Option<&str>, key: Option<&str>) {
110+
let path = split_basis_dict_key(test).unwrap();
111+
assert_eq!(path.basis, basis);
112+
assert_eq!(path.dict, dict);
113+
assert_eq!(path.key, key);
114+
}
115+
116+
#[test]
117+
fn basis() {
118+
// A basis named "Home Wifi"
119+
assert_basis_dict_key(":Home Wifi:", Some("Home Wifi"), None, None);
120+
121+
// :.System: -- A basis named ".System"
122+
assert_basis_dict_key(":.System:", Some(".System"), None, None);
123+
}
124+
125+
#[test]
126+
fn dict() {
127+
// wlan.networks -- A dict named "wlan.networks" in the default basis
128+
assert_basis_dict_key("wlan.networks", None, Some("wlan.networks"), None);
129+
}
130+
131+
#[test]
132+
fn key() {
133+
// wlan.networks/recent -- A dict named "wlan.networks/recent", which may be considered a path, in the default basis. This also describes a key called "recent" in the dict "wlan.networks", depending on whether
134+
assert_basis_dict_key("wlan.networks/recent", None, Some("wlan.networks"), Some("recent"));
135+
}
136+
137+
#[test]
138+
fn key_legacy() {
139+
// wlan.networks/recent -- A dict named "wlan.networks:recent", which may be considered a path, in the default basis. This also describes a key called "recent" in the dict "wlan.networks", depending on whether
140+
assert_basis_dict_key("wlan.networks:recent", None, Some("wlan.networks"), Some("recent"));
141+
}
142+
143+
#[test]
144+
fn dict_in_key() {
145+
// :.System:wlan.networks -- A dict named "wlan.networks" in the basis ".System"
146+
assert_basis_dict_key(":.System:wlan.networks", Some(".System"), Some("wlan.networks"), None);
147+
}
148+
149+
#[test]
150+
fn basis_dict_key() {
151+
// :.System:wlan.networks/recent -- a fully-qualified path, describing a key "recent" in the dict "wlan.networks" in the basis ".System".
152+
assert_basis_dict_key(
153+
":.System:wlan.networks/recent",
154+
Some(".System"),
155+
Some("wlan.networks"),
156+
Some("recent"),
157+
);
158+
}
159+
160+
#[test]
161+
fn basis_dict_key_legacy() {
162+
// :.System:wlan.networks:recent -- a fully-qualified path, describing a key "recent" in the dict "wlan.networks" in the basis ".System".
163+
assert_basis_dict_key(
164+
":.System:wlan.networks:recent",
165+
Some(".System"),
166+
Some("wlan.networks"),
167+
Some("recent"),
168+
);
169+
}
170+
171+
#[test]
172+
fn root() {
173+
// : -- The root, which lists every basis. Files cannot be created here. "Directories" can be created and destroyed, which corresponds to creating and destroying bases.
174+
assert_eq!(split_basis_dict_key(":"), Err(BasisSplitError::Root));
175+
assert_eq!(split_basis_dict_key("/"), Err(BasisSplitError::Root));
176+
}
177+
178+
#[test]
179+
fn all_bases() {
180+
// :: -- An empty basis is a synonym for all bases, so this corresponds to listing all dicts in the root of the default basis.
181+
assert_eq!(split_basis_dict_key("::"), Err(BasisSplitError::All));
182+
assert_eq!(split_basis_dict_key("/:"), Err(BasisSplitError::All));
183+
assert_eq!(split_basis_dict_key(":/"), Err(BasisSplitError::All));
184+
assert_eq!(split_basis_dict_key("//"), Err(BasisSplitError::All));
185+
}
186+
187+
#[test]
188+
fn space_basis() {
189+
// : : -- A basis named " ". Legal, but questionable
190+
assert_basis_dict_key(": :", Some(" "), None, None);
191+
}
192+
193+
#[test]
194+
fn space_dict() {
195+
// -- A dict named " " in the default basis. Legal, but questionable.
196+
assert_basis_dict_key(" ", None, Some(" "), None);
197+
}
198+
199+
#[test]
200+
fn legacy_dict_key() {
201+
// : -- A key named " " in a dict called " ". Legal.
202+
assert_basis_dict_key(" : ", None, Some(" "), Some(" "));
203+
}
204+
205+
#[test]
206+
fn legacy_trailing_separator() {
207+
// baz: -- A dict named "baz" in the default basis with an extra ":" following. Legal.
208+
assert_basis_dict_key("baz:", None, Some("baz"), None);
209+
}
210+
211+
#[test]
212+
fn legacy_trailing_separator_two_dicts() {
213+
// baz:foo: -- Currently illegal, but may become equal to baz:foo in the future.
214+
assert_basis_dict_key("baz:foo:", None, Some("baz:foo"), None);
215+
}
216+
217+
#[test]
218+
fn empty_dict() {
219+
// ::: -- An key named ":" in an empty dict in the default basis. Illegal.
220+
assert_eq!(split_basis_dict_key(":::"), Err(BasisSplitError::EmptyComponent));
221+
}
222+
223+
#[test]
224+
fn empty_key_empty_dict() {
225+
// :::: -- An key named "::" in an empty dict in the default basis. Illegal.
226+
assert_eq!(split_basis_dict_key("::::"), Err(BasisSplitError::EmptyComponent));
227+
}
228+
229+
#[test]
230+
fn key_in_default_basis() {
231+
// ::foo -- A dict "foo" in the default basis.
232+
assert_basis_dict_key("::foo", None, Some("foo"), None);
233+
}
234+
235+
#[test]
236+
fn starts_with_slash() {
237+
assert_basis_dict_key("/test/path", Some("test"), Some("path"), None);
238+
}
239+
240+
#[test]
241+
fn non_legacy_basis() {
242+
assert_basis_dict_key(
243+
"/.System/wlan.networks/recent",
244+
Some(".System"),
245+
Some("wlan.networks"),
246+
Some("recent"),
247+
);
248+
}

0 commit comments

Comments
 (0)