Skip to content

Commit 33adf6d

Browse files
committed
path2: Implement .as_display_str() and .to_display_str()
These functions are for working with a string representation of the path even if it's not UTF-8 encoded. They replace invalid UTF-8 sequences with the replacement char.
1 parent 1dfe508 commit 33adf6d

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

src/libstd/path2/mod.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use clone::Clone;
1616
use iter::Iterator;
1717
use option::{Option, None, Some};
1818
use str;
19-
use str::StrSlice;
19+
use str::{OwnedStr, Str, StrSlice};
2020
use vec;
2121
use vec::{CopyableVector, OwnedCopyableVector, OwnedVector};
2222
use vec::{ImmutableEqVector, ImmutableVector};
@@ -140,6 +140,51 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
140140
/// Returns the path as a byte vector
141141
fn as_vec<'a>(&'a self) -> &'a [u8];
142142

143+
/// Provides the path as a string
144+
///
145+
/// If the path is not UTF-8, invalid sequences will be replaced with the unicode
146+
/// replacement char. This involves allocation.
147+
#[inline]
148+
fn as_display_str<T>(&self, f: &fn(&str) -> T) -> T {
149+
match self.as_str() {
150+
Some(s) => f(s),
151+
None => {
152+
let s = self.to_display_str();
153+
f(s.as_slice())
154+
}
155+
}
156+
}
157+
158+
/// Returns the path as a string
159+
///
160+
/// If the path is not UTF-8, invalid sequences will be replaced with the unicode
161+
/// replacement char. This involves allocation.
162+
///
163+
/// This is similar to `as_display_str()` except it will always allocate a new ~str.
164+
fn to_display_str(&self) -> ~str {
165+
// FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str
166+
// This is a truly horrifically bad implementation, done as a functionality stopgap until
167+
// we have a proper utf-8 decoder. I don't really want to write one here.
168+
static REPLACEMENT_CHAR: char = '\uFFFD';
169+
170+
let mut v = self.as_vec();
171+
let mut s = str::with_capacity(v.len());
172+
while !v.is_empty() {
173+
let w = str::utf8_char_width(v[0]);
174+
if w == 0u {
175+
s.push_char(REPLACEMENT_CHAR);
176+
v = v.slice_from(1);
177+
} else if v.len() < w || !str::is_utf8(v.slice_to(w)) {
178+
s.push_char(REPLACEMENT_CHAR);
179+
v = v.slice_from(1);
180+
} else {
181+
s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) });
182+
v = v.slice_from(w);
183+
}
184+
}
185+
s
186+
}
187+
143188
/// Returns the directory component of `self`, as a byte vector (with no trailing separator).
144189
/// If `self` has no directory component, returns ['.'].
145190
fn dirname<'a>(&'a self) -> &'a [u8];

src/libstd/path2/posix.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,32 @@ mod tests {
597597
})
598598
}
599599
600+
#[test]
601+
fn test_display_str() {
602+
assert_eq!(Path::from_str("foo").to_display_str(), ~"foo");
603+
assert_eq!(Path::from_vec(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD");
604+
assert_eq!(Path::from_vec(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar");
605+
606+
let mut called = false;
607+
do Path::from_str("foo").as_display_str |s| {
608+
assert_eq!(s, "foo");
609+
called = true;
610+
};
611+
assert!(called);
612+
called = false;
613+
do Path::from_vec(b!("foo", 0x80)).as_display_str |s| {
614+
assert_eq!(s, "foo\uFFFD");
615+
called = true;
616+
};
617+
assert!(called);
618+
called = false;
619+
do Path::from_vec(b!("foo", 0xff, "bar")).as_display_str |s| {
620+
assert_eq!(s, "foo\uFFFDbar");
621+
called = true;
622+
};
623+
assert!(called);
624+
}
625+
600626
#[test]
601627
fn test_components() {
602628
macro_rules! t(

src/libstd/path2/windows.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,16 @@ impl GenericPath for Path {
349349
self.repr.as_bytes()
350350
}
351351

352+
#[inline]
353+
fn as_display_str<T>(&self, f: &fn(&str) -> T) -> T {
354+
f(self.repr.as_slice())
355+
}
356+
357+
#[inline]
358+
fn to_display_str(&self) -> ~str {
359+
self.repr.clone()
360+
}
361+
352362
#[inline]
353363
fn dirname<'a>(&'a self) -> &'a [u8] {
354364
self.dirname_str().unwrap().as_bytes()
@@ -1339,6 +1349,18 @@ mod tests {
13391349
Path::from_vec(b!("hello", 0x80, ".txt"));
13401350
}
13411351
1352+
#[test]
1353+
fn test_display_str() {
1354+
assert_eq!(Path::from_str("foo").to_display_str(), ~"foo");
1355+
1356+
let mut called = false;
1357+
do Path::from_str("foo").as_display_str |s| {
1358+
assert_eq!(s, "foo");
1359+
called = true;
1360+
};
1361+
assert!(called);
1362+
}
1363+
13421364
#[test]
13431365
fn test_components() {
13441366
macro_rules! t(

0 commit comments

Comments
 (0)