faster_path 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
data/src/dirname.rs CHANGED
@@ -1,26 +1,66 @@
1
- extern crate memchr;
2
- use self::memchr::memrchr;
3
- use path_parsing::{SEP, SEP_STR, last_non_sep_i, last_non_sep_i_before};
1
+ use std::str;
2
+ use path_parsing::{find_last_sep_pos, find_last_non_sep_pos};
4
3
 
5
4
  pub fn dirname(path: &str) -> &str {
6
- if path.is_empty() { return "."; }
7
- let non_sep_i = last_non_sep_i(path);
8
- if non_sep_i == -1 { return *SEP_STR; }
9
- return match memrchr(SEP, &path.as_bytes()[..non_sep_i as usize]) {
10
- None => ".",
11
- Some(0) => *SEP_STR,
12
- Some(sep_i) => {
13
- let non_sep_i2 = last_non_sep_i_before(path, sep_i as isize);
14
- if non_sep_i2 != -1 {
15
- &path[..(non_sep_i2 + 1) as usize]
16
- } else {
17
- *SEP_STR
18
- }
19
- }
5
+ let bytes = path.as_bytes();
6
+ let mut last_slash_pos = match find_last_sep_pos(bytes) {
7
+ Some(pos) => pos,
8
+ _ => return ".",
9
+ };
10
+ // Skip trailing slashes.
11
+ if last_slash_pos == bytes.len() - 1 {
12
+ let last_non_slash_pos = match find_last_non_sep_pos(&bytes[..last_slash_pos]) {
13
+ Some(pos) => pos,
14
+ _ => return "/"
15
+ };
16
+ last_slash_pos = match find_last_sep_pos(&bytes[..last_non_slash_pos]) {
17
+ Some(pos) => pos,
18
+ _ => return "."
19
+ };
20
+ };
21
+ if let Some(end) = find_last_non_sep_pos(&bytes[..last_slash_pos]) {
22
+ &path[..end + 1]
23
+ } else {
24
+ "/"
20
25
  }
21
26
  }
22
27
 
23
28
  #[test]
24
- fn returns_dot_for_empty_string(){
25
- assert_eq!(dirname(""), ".".to_string());
29
+ fn absolute() {
30
+ assert_eq!(dirname("/a/b///c"), "/a/b");
31
+ }
32
+
33
+ #[test]
34
+ fn trailing_slashes_absolute() {
35
+ assert_eq!(dirname("/a/b///c//////"), "/a/b");
36
+ }
37
+
38
+ #[test]
39
+ fn relative() {
40
+ assert_eq!(dirname("b///c"), "b");
41
+ }
42
+
43
+ #[test]
44
+ fn trailing_slashes_relative() {
45
+ assert_eq!(dirname("b/c//"), "b");
46
+ }
47
+
48
+ #[test]
49
+ fn root() {
50
+ assert_eq!(dirname("//c"), "/");
51
+ }
52
+
53
+ #[test]
54
+ fn trailing_slashes_root() {
55
+ assert_eq!(dirname("//c//"), "/");
56
+ }
57
+
58
+ #[test]
59
+ fn trailing_slashes_relative_root() {
60
+ assert_eq!(dirname("c//"), ".");
61
+ }
62
+
63
+ #[test]
64
+ fn returns_dot_for_empty_string() {
65
+ assert_eq!(dirname(""), ".");
26
66
  }
data/src/extname.rs CHANGED
@@ -1,63 +1,25 @@
1
- use path_parsing::SEP;
2
1
  use std::str;
2
+ use path_parsing::{SEP, find_last_non_sep_pos};
3
3
 
4
- struct ExtnameCoords {
5
- word: bool,
6
- pred: bool,
7
- dot: bool,
8
- start: usize,
9
- end: usize,
10
- }
11
-
12
- impl ExtnameCoords {
13
- pub fn dec(&mut self) {
14
- self.start -= 1;
15
- if !self.word {
16
- self.end -= 1;
17
- }
18
- }
19
- }
20
-
21
- pub fn extname(pth: &str) -> &str {
22
- let path = pth.as_bytes();
23
- let mut extname = ExtnameCoords {
24
- word: false,
25
- pred: false,
26
- dot: false,
27
- start: path.len(),
28
- end: path.len(),
4
+ pub fn extname(path: &str) -> &str {
5
+ let end = match find_last_non_sep_pos(path.as_bytes()) {
6
+ Some(pos) => pos + 1,
7
+ _ => return "",
29
8
  };
30
-
31
- for &item in path.iter().rev() {
32
- if (item == b'.' && !extname.dot) || item == SEP {
33
- if item == SEP && extname.word {
34
- return ""
35
- }
36
-
37
- if !extname.pred {
38
- extname.dec();
39
- }
40
-
41
- if extname.word {
42
- extname.dot = true;
43
- }
44
- } else {
45
- if extname.dot {
46
- extname.pred = true;
47
- break;
48
- } else {
49
- extname.word = true;
50
- }
51
-
52
- if !extname.pred {
53
- extname.dec()
9
+ let bytes = &path.as_bytes()[..end];
10
+ for (pos, c) in bytes.iter().enumerate().rev() {
11
+ match *c {
12
+ b'.' => {
13
+ let prev = bytes.get(pos - 1);
14
+ if pos == end - 1 || prev == None || prev == Some(&SEP) {
15
+ return "";
16
+ } else {
17
+ return &path[pos..end]
18
+ };
54
19
  }
20
+ SEP => return "",
21
+ _ => {}
55
22
  }
56
- }
57
-
58
- if !extname.pred {
59
- return "";
60
- }
61
-
62
- str::from_utf8(&path[extname.start..extname.end]).unwrap_or("")
23
+ };
24
+ ""
63
25
  }
data/src/helpers.rs CHANGED
@@ -1,7 +1,9 @@
1
1
  use ruru::{RString, Object, Class, AnyObject};
2
2
  extern crate ruby_sys;
3
3
  use debug::RubyDebugInfo;
4
- use pathname::Pathname;
4
+ use ruru;
5
+
6
+ type MaybeString = Result<ruru::RString, ruru::result::Error>;
5
7
 
6
8
  pub trait TryFrom<T>: Sized {
7
9
  type Error;
@@ -23,21 +25,24 @@ pub fn anyobject_to_string(item: AnyObject) -> Result<String, RubyDebugInfo> {
23
25
  if Class::from_existing("String").case_equals(result) {
24
26
  return Ok(RString::from(result.value()).to_string())
25
27
  }
26
-
28
+
27
29
  if Class::from_existing("Pathname").case_equals(result) {
28
30
  return Ok(result.instance_variable_get("@path").
29
31
  try_convert_to::<RString>().
30
32
  unwrap_or(RString::new("")).
31
33
  to_string())
32
34
  }
33
-
35
+
34
36
  if result.respond_to("to_path") {
35
- return Ok(Pathname::from(result.send("to_path", None).value()).
36
- instance_variable_get("@path").
37
- try_convert_to::<RString>().
38
- unwrap_or(RString::new("")).
39
- to_string())
37
+ return Ok(RString::from(result.send("to_path", None).value()).to_string())
40
38
  }
41
39
 
42
40
  Ok(RString::from(result.send("to_s", None).value()).to_string())
43
41
  }
42
+
43
+ pub fn to_str(maybe_string: &MaybeString) -> &str {
44
+ match maybe_string {
45
+ &Ok(ref ruru_string) => ruru_string.to_str(),
46
+ &Err(_) => "",
47
+ }
48
+ }
data/src/lib.rs CHANGED
@@ -25,13 +25,14 @@ mod pathname_sys;
25
25
  mod plus;
26
26
  mod prepend_prefix;
27
27
  pub mod rust_arch_bits;
28
+ mod memrnchr;
28
29
  mod path_parsing;
29
30
  mod relative_path_from;
30
31
 
31
32
  use pathname::Pathname;
32
33
  use pathname_sys::raise;
33
34
 
34
- use ruru::{Module, Object, RString, Boolean, Array, AnyObject};
35
+ use ruru::{Module, Object, RString, Boolean, AnyObject};
35
36
 
36
37
  use pathname_sys::*;
37
38
 
@@ -54,14 +55,16 @@ methods!(
54
55
  }
55
56
 
56
57
  fn pub_children(pth: RString, with_dir: Boolean) -> AnyObject {
57
- pathname::pn_children(pth, with_dir)
58
+ pathname::pn_children(pth, with_dir).
59
+ map_err(|e| raise(e) ).unwrap()
58
60
  }
59
61
 
60
62
  fn pub_children_compat(pth: RString, with_dir: Boolean) -> AnyObject {
61
- pathname::pn_children_compat(pth, with_dir)
63
+ pathname::pn_children_compat(pth, with_dir).
64
+ map_err(|e| raise(e) ).unwrap()
62
65
  }
63
66
 
64
- fn pub_chop_basename(pth: RString) -> Array {
67
+ fn pub_chop_basename(pth: RString) -> AnyObject {
65
68
  pathname::pn_chop_basename(pth)
66
69
  }
67
70
 
@@ -99,12 +102,14 @@ methods!(
99
102
 
100
103
  // pub_entries returns an array of String objects
101
104
  fn pub_entries(pth: RString) -> AnyObject {
102
- pathname::pn_entries(pth)
105
+ pathname::pn_entries(pth).
106
+ map_err(|e| raise(e) ).unwrap()
103
107
  }
104
108
 
105
109
  // pub_entries_compat returns an array of Pathname objects
106
110
  fn pub_entries_compat(pth: RString) -> AnyObject {
107
- pathname::pn_entries_compat(pth)
111
+ pathname::pn_entries_compat(pth).
112
+ map_err(|e| raise(e) ).unwrap()
108
113
  }
109
114
 
110
115
  fn pub_extname(pth: RString) -> RString {
@@ -163,10 +168,13 @@ pub extern "C" fn Init_faster_pathname() {
163
168
  itself.def_self("absolute?", pub_is_absolute);
164
169
  itself.def_self("add_trailing_separator", pub_add_trailing_separator);
165
170
  itself.def_self("del_trailing_separator", pub_del_trailing_separator);
171
+ itself.def_self("chop_basename", pub_chop_basename);
166
172
  itself.def_self("cleanpath_aggressive", pub_cleanpath_aggressive);
167
173
  itself.def_self("cleanpath_conservative", pub_cleanpath_conservative);
168
174
  itself.def_self("directory?", pub_is_directory);
169
175
  itself.def_self("dirname", pub_dirname);
176
+ itself.def_self("entries", pub_entries);
177
+ itself.def_self("entries_compat", pub_entries_compat);
170
178
  itself.def_self("extname", pub_extname);
171
179
  itself.def_self("has_trailing_separator?", pub_has_trailing_separator);
172
180
  //itself.def_self("join", pub_join);
@@ -182,8 +190,5 @@ pub extern "C" fn Init_faster_pathname() {
182
190
  itself.def_self("basename", pub_basename);
183
191
  itself.def_self("children", pub_children);
184
192
  itself.def_self("children_compat", pub_children_compat);
185
- itself.def_self("chop_basename", pub_chop_basename);
186
- itself.def_self("entries", pub_entries);
187
- itself.def_self("entries_compat", pub_entries_compat);
188
193
  });
189
194
  }
data/src/memrnchr.rs ADDED
@@ -0,0 +1,70 @@
1
+ // The code below is based on the fallback `memrchr` implementation from the respective crate.
2
+ //
3
+ // We use this mainly to skip repeated `/`. If there is only one slash, `memrnchr` performs the same
4
+ // as a naive version (e.g. `rposition`). However, it is much faster in pathological cases.
5
+
6
+ use std::mem::size_of;
7
+
8
+ // Returns the byte offset of the last byte that is NOT equal to the given one.
9
+ #[inline(always)]
10
+ pub fn memrnchr(x: u8, text: &[u8]) -> Option<usize> {
11
+ // Scan for a single byte value by reading two `usize` words at a time.
12
+ //
13
+ // Split `text` in three parts
14
+ // - unaligned tail, after the last word aligned address in text
15
+ // - body, scan by 2 words at a time
16
+ // - the first remaining bytes, < 2 word size
17
+ let len = text.len();
18
+ let ptr = text.as_ptr();
19
+
20
+ // search to an aligned boundary
21
+ let end_align = (ptr as usize + len) & (size_of::<usize>() - 1);
22
+ let mut offset;
23
+ if end_align > 0 {
24
+ offset = if end_align >= len { 0 } else { len - end_align };
25
+ if let Some(index) = memrnchr_naive(x, &text[offset..]) {
26
+ return Some(offset + index);
27
+ }
28
+ } else {
29
+ offset = len;
30
+ }
31
+
32
+ // search the body of the text
33
+ let repeated_x = repeat_byte(x);
34
+ while offset >= 2 * size_of::<usize>() {
35
+ debug_assert_eq!((ptr as usize + offset) % size_of::<usize>(), 0);
36
+ unsafe {
37
+ let u = *(ptr.offset(offset as isize - 2 * size_of::<usize>() as isize) as *const usize);
38
+ let v = *(ptr.offset(offset as isize - size_of::<usize>() as isize) as *const usize);
39
+ if u & repeated_x != usize::max_value() || v & repeated_x != usize::max_value() {
40
+ break;
41
+ }
42
+ }
43
+ offset -= 2 * size_of::<usize>();
44
+ }
45
+
46
+ // find the byte before the point the body loop stopped
47
+ memrnchr_naive(x, &text[..offset])
48
+ }
49
+
50
+ #[inline(always)]
51
+ fn memrnchr_naive(x: u8, text: &[u8]) -> Option<usize> {
52
+ text.iter().rposition(|c| *c != x)
53
+ }
54
+
55
+ #[cfg(target_pointer_width = "32")]
56
+ #[inline]
57
+ fn repeat_byte(b: u8) -> usize {
58
+ let mut rep = (b as usize) << 8 | b as usize;
59
+ rep = rep << 16 | rep;
60
+ rep
61
+ }
62
+
63
+ #[cfg(target_pointer_width = "64")]
64
+ #[inline]
65
+ fn repeat_byte(b: u8) -> usize {
66
+ let mut rep = (b as usize) << 8 | b as usize;
67
+ rep = rep << 16 | rep;
68
+ rep = rep << 32 | rep;
69
+ rep
70
+ }
data/src/path_parsing.rs CHANGED
@@ -1,4 +1,7 @@
1
1
  extern crate memchr;
2
+
3
+ use self::memchr::{memchr, memrchr};
4
+ use memrnchr::memrnchr;
2
5
  use std::path::MAIN_SEPARATOR;
3
6
  use std::str;
4
7
 
@@ -7,20 +10,26 @@ lazy_static! {
7
10
  pub static ref SEP_STR: &'static str = str::from_utf8(&[SEP]).unwrap();
8
11
  }
9
12
 
10
- // Returns the byte offset of the last byte preceding a MAIN_SEPARATOR.
11
- pub fn last_non_sep_i(path: &str) -> isize {
12
- last_non_sep_i_before(path, path.len() as isize - 1)
13
+ // Returns the byte offset of the last byte that equals MAIN_SEPARATOR.
14
+ #[inline(always)]
15
+ pub fn find_last_dot_pos(bytes: &[u8]) -> Option<usize> {
16
+ memrchr(b'.', bytes)
17
+ }
18
+
19
+ // Returns the byte offset of the last byte that equals MAIN_SEPARATOR.
20
+ #[inline(always)]
21
+ pub fn find_last_sep_pos(bytes: &[u8]) -> Option<usize> {
22
+ memrchr(SEP, bytes)
23
+ }
24
+
25
+ // Returns the byte offset of the last byte that is not MAIN_SEPARATOR.
26
+ #[inline(always)]
27
+ pub fn find_last_non_sep_pos(bytes: &[u8]) -> Option<usize> {
28
+ memrnchr(SEP, bytes)
13
29
  }
14
30
 
15
- // Returns the byte offset of the last byte preceding a MAIN_SEPARATOR before the given end offset.
16
- pub fn last_non_sep_i_before(path: &str, end: isize) -> isize {
17
- // Works with bytes directly because MAIN_SEPARATOR is always in the ASCII 7-bit range so we can
18
- // avoid the overhead of full UTF-8 processing.
19
- let ptr = path.as_ptr();
20
- let mut i = end;
21
- while i >= 0 {
22
- if unsafe { *ptr.offset(i) } != SEP { break; };
23
- i -= 1;
24
- }
25
- i
31
+ // Whether the given byte sequence contains a MAIN_SEPARATOR.
32
+ #[inline(always)]
33
+ pub fn contains_sep(bytes: &[u8]) -> bool {
34
+ memchr(SEP, bytes) != None
26
35
  }
data/src/pathname.rs CHANGED
@@ -8,8 +8,9 @@ use extname;
8
8
  use plus;
9
9
  use relative_path_from;
10
10
  use debug;
11
- use helpers::TryFrom;
11
+ use helpers::{TryFrom, to_str};
12
12
  use pathname_sys::null_byte_check;
13
+ use path_parsing::{SEP, find_last_non_sep_pos};
13
14
 
14
15
  use ruru;
15
16
  use ruru::{
@@ -40,7 +41,7 @@ impl Pathname {
40
41
  pub fn new(path: &str) -> Pathname {
41
42
  let mut instance = Class::from_existing("Pathname").allocate();
42
43
  instance.instance_variable_set("@path", RString::new(path).to_any_object());
43
-
44
+
44
45
  Pathname { value: instance.value() }
45
46
  }
46
47
 
@@ -58,14 +59,14 @@ impl Pathname {
58
59
  )
59
60
  };
60
61
 
61
- if null_byte_check(path.value()) {
62
+ if null_byte_check(path.value()) {
62
63
  return Err( Exception::new("ArgumentError", Some("pathname contains null byte")) )
63
64
  }
64
65
 
65
66
  // if it crashes then dup the path string here before assigning to @path
66
67
  let mut instance = Class::from_existing("Pathname").allocate();
67
68
  instance.instance_variable_set("@path", RString::from(pth).to_any_object());
68
-
69
+
69
70
  Ok(Pathname { value: instance.value() })
70
71
  }
71
72
 
@@ -84,11 +85,11 @@ impl TryFrom<AnyObject> for Pathname {
84
85
  type Error = debug::RubyDebugInfo;
85
86
  fn try_from(obj: AnyObject) -> Result<Pathname, Self::Error> {
86
87
  if Class::from_existing("String").case_equals(&obj) {
87
- Ok(Pathname::new(&RString::from(obj.value()).to_string()))
88
+ Ok(Pathname::new(&RString::from(obj.value()).to_str()))
88
89
  } else if Class::from_existing("Pathname").case_equals(&obj) {
89
90
  Ok(Pathname::from(obj.value()))
90
91
  } else if obj.respond_to("to_path") {
91
- Ok(Pathname::from(obj.send("to_path", None).value()))
92
+ Ok(Pathname::new(&RString::from(obj.send("to_path", None).value()).to_str()))
92
93
  } else {
93
94
  Err(Self::Error::from(obj))
94
95
  }
@@ -114,7 +115,7 @@ impl VerifiedObject for Pathname {
114
115
  }
115
116
 
116
117
  pub fn pn_add_trailing_separator(pth: MaybeString) -> RString {
117
- let p = pth.ok().unwrap();
118
+ let p = pth.unwrap();
118
119
  let x = format!("{}{}", p.to_str(), "a");
119
120
  match x.rsplit_terminator(MAIN_SEPARATOR).next() {
120
121
  Some("a") => p,
@@ -123,34 +124,26 @@ pub fn pn_add_trailing_separator(pth: MaybeString) -> RString {
123
124
  }
124
125
 
125
126
  pub fn pn_is_absolute(pth: MaybeString) -> Boolean {
126
- Boolean::new(match pth.ok().unwrap_or(RString::new("")).to_str().chars().next() {
127
- Some(c) => c == MAIN_SEPARATOR,
128
- None => false
129
- })
127
+ Boolean::new(to_str(&pth).as_bytes().get(0) == Some(&SEP))
130
128
  }
131
129
 
132
130
  // pub fn pn_ascend(){}
133
131
 
134
132
  pub fn pn_basename(pth: MaybeString, ext: MaybeString) -> RString {
135
- RString::new(
136
- basename::basename(
137
- pth.ok().unwrap_or(RString::new("")).to_str(),
138
- ext.ok().unwrap_or(RString::new("")).to_str()
139
- )
140
- )
133
+ RString::new(basename::basename(to_str(&pth), to_str(&ext)))
141
134
  }
142
135
 
143
- pub fn pn_children(pth: MaybeString, with_dir: MaybeBoolean) -> AnyObject {
144
- let val = pth.ok().unwrap_or(RString::new("."));
145
- let val = val.to_str();
136
+ pub fn pn_children(pth: MaybeString, with_dir: MaybeBoolean) -> Result<AnyObject, Exception> {
137
+ let path = pth.unwrap_or(RString::new("."));
138
+ let path = path.to_str();
146
139
 
147
- if let Ok(entries) = fs::read_dir(val) {
148
- let mut with_directory = with_dir.ok().unwrap_or(Boolean::new(true)).to_bool();
149
- if val == "." {
140
+ if let Ok(entries) = fs::read_dir(path) {
141
+ let mut with_directory = with_dir.unwrap_or(Boolean::new(true)).to_bool();
142
+ if path == "." {
150
143
  with_directory = false;
151
144
  }
152
145
 
153
- let mut arr = Array::new();
146
+ let mut arr = Array::with_capacity(entries.size_hint().1.unwrap_or(0));
154
147
  for entry in entries {
155
148
  if with_directory {
156
149
  match entry {
@@ -165,25 +158,23 @@ pub fn pn_children(pth: MaybeString, with_dir: MaybeBoolean) -> AnyObject {
165
158
  }
166
159
  }
167
160
 
168
- arr.to_any_object()
161
+ Ok(arr.to_any_object())
169
162
  } else {
170
- // TODO: When ruru exceptions are available switch the exception logic
171
- // from the Ruby side to the Rust side
172
- NilClass::new().to_any_object()
163
+ let msg = format!("No such file or directory @ dir_initialize - {}", path);
164
+ Err(Exception::new("Errno::NOENT", Some(&msg)))
173
165
  }
174
166
  }
175
167
 
176
- pub fn pn_children_compat(pth: MaybeString, with_dir: MaybeBoolean) -> AnyObject {
177
- let val = pth.ok().unwrap_or(RString::new("."));
178
- let val = val.to_str();
168
+ pub fn pn_children_compat(pth: MaybeString, with_dir: MaybeBoolean) -> Result<AnyObject, Exception> {
169
+ let path = to_str(&pth);
179
170
 
180
- if let Ok(entries) = fs::read_dir(val) {
181
- let mut with_directory = with_dir.ok().unwrap_or(Boolean::new(true)).to_bool();
182
- if val == "." {
171
+ if let Ok(entries) = fs::read_dir(path) {
172
+ let mut with_directory = with_dir.unwrap_or(Boolean::new(true)).to_bool();
173
+ if path == "." {
183
174
  with_directory = false;
184
175
  }
185
176
 
186
- let mut arr = Array::new();
177
+ let mut arr = Array::with_capacity(entries.size_hint().1.unwrap_or(0));
187
178
  for entry in entries {
188
179
  if with_directory {
189
180
  if let Ok(v) = entry {
@@ -196,82 +187,60 @@ pub fn pn_children_compat(pth: MaybeString, with_dir: MaybeBoolean) -> AnyObject
196
187
  }
197
188
  }
198
189
 
199
- arr.to_any_object()
190
+ Ok(arr.to_any_object())
200
191
  } else {
201
- // TODO: When ruru exceptions are available switch the exception logic
202
- // from the Ruby side to the Rust side
203
- NilClass::new().to_any_object()
192
+ let msg = format!("No such file or directory @ dir_initialize - {}", path);
193
+ Err(Exception::new("Errno::NOENT", Some(&msg)))
204
194
  }
205
195
  }
206
196
 
207
- pub fn pn_chop_basename(pth: MaybeString) -> Array {
208
- let mut arr = Array::with_capacity(2);
209
- let pth = pth.ok().unwrap_or(RString::new(""));
210
- let results = chop_basename::chop_basename(pth.to_str());
211
- match results {
197
+ pub fn pn_chop_basename(pth: MaybeString) -> AnyObject {
198
+ match chop_basename::chop_basename(to_str(&pth)) {
212
199
  Some((dirname, basename)) => {
213
- arr.push(RString::new(&dirname[..]));
214
- arr.push(RString::new(&basename[..]));
215
- arr
200
+ let mut arr = Array::with_capacity(2);
201
+ arr.push(RString::new(&dirname));
202
+ arr.push(RString::new(&basename));
203
+ arr.to_any_object()
216
204
  },
217
- None => arr
205
+ None => NilClass::new().to_any_object()
218
206
  }
219
207
  }
220
208
 
221
209
  // pub fn pn_cleanpath(pth: MaybeString){}
222
210
 
223
211
  pub fn pn_cleanpath_aggressive(pth: MaybeString) -> RString {
224
- let path = cleanpath_aggressive::cleanpath_aggressive(
225
- pth.ok().unwrap_or(RString::new("")).to_str()
226
- );
227
-
228
- RString::new(&path)
212
+ RString::new(&cleanpath_aggressive::cleanpath_aggressive(to_str(&pth)))
229
213
  }
230
214
 
231
215
  pub fn pn_cleanpath_conservative(pth: MaybeString) -> RString {
232
- let path = cleanpath_conservative::cleanpath_conservative(
233
- pth.ok().unwrap_or(RString::new("")).to_str()
234
- );
235
-
236
- RString::new(&path)
216
+ RString::new(&cleanpath_conservative::cleanpath_conservative(to_str(&pth)))
237
217
  }
238
218
 
239
219
  pub fn pn_del_trailing_separator(pth: MaybeString) -> RString {
240
- if let &Ok(ref path) = &pth {
241
- let path = path.to_str();
242
-
243
- if !path.is_empty() {
244
- let path = path.trim_right_matches('/');
245
-
246
- if path.is_empty() {
247
- return RString::new("/");
248
- } else {
249
- return RString::new(path);
250
- }
220
+ {
221
+ let path = to_str(&pth);
222
+ if path.is_empty() {
223
+ return RString::new("/");
224
+ }
225
+ let pos = match find_last_non_sep_pos(path.as_bytes()) {
226
+ Some(pos) => pos,
227
+ None => return RString::new("/"),
228
+ };
229
+ if pos != path.len() - 1 {
230
+ return RString::new(&path[..pos + 1]);
251
231
  }
252
- } else {
253
- return RString::new("");
254
232
  }
255
-
256
233
  pth.unwrap()
257
234
  }
258
235
 
259
236
  // pub fn pn_descend(){}
260
237
 
261
238
  pub fn pn_is_directory(pth: MaybeString) -> Boolean {
262
- Boolean::new(
263
- Path::new(
264
- pth.ok().unwrap_or(RString::new("")).to_str()
265
- ).is_dir()
266
- )
239
+ Boolean::new(Path::new(to_str(&pth)).is_dir())
267
240
  }
268
241
 
269
242
  pub fn pn_dirname(pth: MaybeString) -> RString {
270
- RString::new(
271
- dirname::dirname(
272
- pth.ok().unwrap_or(RString::new("")).to_str()
273
- )
274
- )
243
+ RString::new(dirname::dirname(to_str(&pth)))
275
244
  }
276
245
 
277
246
  // pub fn pn_each_child(){}
@@ -280,60 +249,54 @@ pub fn pn_dirname(pth: MaybeString) -> RString {
280
249
  // NilClass::new()
281
250
  // }
282
251
 
283
- pub fn pn_entries(pth: MaybeString) -> AnyObject {
284
- if let Ok(files) = fs::read_dir(pth.ok().unwrap_or(RString::new("")).to_str()) {
285
- let mut arr = Array::new();
252
+ pub fn pn_entries(pth: MaybeString) -> Result<AnyObject, Exception> {
253
+ let path = to_str(&pth);
254
+ if let Ok(files) = fs::read_dir(path) {
255
+ let mut arr = Array::with_capacity(files.size_hint().1.unwrap_or(0) + 2);
286
256
 
287
257
  arr.push(RString::new("."));
288
258
  arr.push(RString::new(".."));
289
259
 
290
260
  for file in files {
291
- let file_name_str = file.unwrap().file_name().into_string().unwrap();
292
- arr.push(RString::new(&file_name_str[..]));
261
+ arr.push(RString::new(file.unwrap().file_name().to_str().unwrap()));
293
262
  }
294
263
 
295
- arr.to_any_object()
264
+ Ok(arr.to_any_object())
296
265
  } else {
297
- // TODO: When ruru exceptions are available switch the exception logic
298
- // from the Ruby side to the Rust side
299
- NilClass::new().to_any_object()
266
+ let msg = format!("No such file or directory @ dir_initialize - {}", path);
267
+ Err(Exception::new("Errno::NOENT", Some(&msg)))
300
268
  }
301
269
  }
302
270
 
303
- pub fn pn_entries_compat(pth: MaybeString) -> AnyObject {
304
- if let Ok(files) = fs::read_dir(pth.ok().unwrap_or(RString::new("")).to_str()) {
305
- let mut arr = Array::new();
271
+ pub fn pn_entries_compat(pth: MaybeString) -> Result<AnyObject, Exception> {
272
+ let path = to_str(&pth);
273
+ if let Ok(files) = fs::read_dir(path) {
274
+ let mut arr = Array::with_capacity(files.size_hint().1.unwrap_or(0) + 2);
306
275
 
307
276
  arr.push(Pathname::new("."));
308
277
  arr.push(Pathname::new(".."));
309
278
 
310
279
  for file in files {
311
- let file_name_str = file.unwrap().file_name().into_string().unwrap();
312
- arr.push(Pathname::new(&file_name_str));
280
+ arr.push(Pathname::new(file.unwrap().file_name().to_str().unwrap()));
313
281
  }
314
282
 
315
- arr.to_any_object()
283
+ Ok(arr.to_any_object())
316
284
  } else {
317
- // TODO: When ruru exceptions are available switch the exception logic
318
- // from the Ruby side to the Rust side
319
- NilClass::new().to_any_object()
285
+ let msg = format!("No such file or directory @ dir_initialize - {}", path);
286
+ Err(Exception::new("Errno::NOENT", Some(&msg)))
320
287
  }
321
288
  }
322
289
 
323
290
  pub fn pn_extname(pth: MaybeString) -> RString {
324
- RString::new(
325
- extname::extname(pth.ok().unwrap_or(RString::new("")).to_str())
326
- )
291
+ RString::new(extname::extname(to_str(&pth)))
327
292
  }
328
293
 
329
294
  // pub fn pn_find(pth: MaybeString, ignore_error: Boolean){}
330
295
 
331
296
  pub fn pn_has_trailing_separator(pth: MaybeString) -> Boolean {
332
- let v = pth.ok().unwrap_or(RString::new(""));
333
- match chop_basename::chop_basename(v.to_str()) {
334
- Some((a,b)) => {
335
- Boolean::new(a.len() + b.len() < v.to_str().len())
336
- },
297
+ let v = to_str(&pth);
298
+ match chop_basename::chop_basename(v) {
299
+ Some((a,b)) => Boolean::new(a.len() + b.len() < v.len()),
337
300
  _ => Boolean::new(false)
338
301
  }
339
302
  }
@@ -353,13 +316,13 @@ pub fn pn_join(args: MaybeArray) -> AnyObject {
353
316
 
354
317
  let item = args.pop();
355
318
  result = plus::plus_paths(&anyobject_to_string(item).unwrap(), &result);
356
- if result.chars().next() == Some(MAIN_SEPARATOR) {
319
+ if result.as_bytes().get(0) == Some(&SEP) {
357
320
  return Pathname::new(&result).to_any_object()
358
321
  }
359
322
 
360
323
  qty -= 1;
361
324
  }
362
-
325
+
363
326
  let result = plus::plus_paths(&path_self, &result);
364
327
 
365
328
  Pathname::new(&result).to_any_object()
@@ -374,23 +337,17 @@ pub fn pn_join(args: MaybeArray) -> AnyObject {
374
337
  // pub fn pn_parent(pth: MaybeString){}
375
338
 
376
339
  pub fn pn_plus(pth1: MaybeString, pth2: MaybeString) -> RString {
377
- RString::new(
378
- &plus::plus_paths(
379
- pth1.ok().unwrap_or(RString::new("")).to_str(),
380
- pth2.ok().unwrap_or(RString::new("")).to_str()
381
- )[..]
382
- )
340
+ RString::new(&plus::plus_paths(to_str(&pth1), to_str(&pth2)))
383
341
  }
384
342
 
385
343
  // pub fn pn_prepend_prefix(prefix: MaybeString, relpath: MaybeString){}
386
344
 
387
345
  pub fn pn_is_relative(pth: MaybeString) -> Boolean {
388
- Boolean::new(
389
- match pth.ok().unwrap_or(RString::new(&MAIN_SEPARATOR.to_string()[..])).to_str().chars().next() {
390
- Some(c) => c != MAIN_SEPARATOR,
391
- None => true
392
- }
393
- )
346
+ let path = match &pth {
347
+ &Ok(ref ruru_string) => ruru_string.to_str(),
348
+ &Err(_) => return Boolean::new(false),
349
+ };
350
+ Boolean::new(path.as_bytes().get(0) != Some(&SEP))
394
351
  }
395
352
 
396
353
  // pub fn pn_root(pth: MaybeString){}