Skip to content

Commit 67a2f3f

Browse files
committed
Fix errors during unicode handling
1 parent 5b3526c commit 67a2f3f

File tree

6 files changed

+124
-5
lines changed

6 files changed

+124
-5
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pretty-copy"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
edition = "2021"
55
authors = ["Anna-Sophie Zaitsewa"]
66
description = "Multi-purpose copying utility which allows to see progress of copying operations"

src/copy.rs

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ pub fn copy_directory(source: &str, target: &str, args: &Args) -> bool{
152152
let reader_proxy = get_reader_proxy_for_url(source).unwrap();
153153
let is_new_dir = if writer_proxy.is_directory(target){
154154
target_path = writer_proxy.join_path(&target_path, &reader_proxy.dirname(source));
155+
writer_proxy.make_directory(&target_path);
155156
false
156157
} else {
157158
writer_proxy.make_directory(target);

src/progress.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod dummy;
44
pub trait ProgressDisplay {
55
fn new() -> Self where Self: Sized;
66
fn set_progress(&mut self, status: &str, bytes_out: usize);
7+
#[allow(dead_code)]
78
fn update_status(&mut self, new_status: &str);
89
fn add_bytes_written(&mut self, bytes_written: usize);
910
fn set_size(&mut self, bytes_total: usize);

src/progress/console.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use colored::Colorize;
33
use termion::terminal_size;
44

55
use crate::progress::ProgressDisplay;
6-
use crate::utils::get_time;
6+
use crate::utils::{get_time, safe_string_trim_left, safe_string_trim_right};
77

88
const MAX_STATUS_WIDTH: u16 = 128;
99
const STATUS_WIDTH_FACTOR: f32 = 0.2;
@@ -26,7 +26,8 @@ fn pad_status(status: String, max_width: u16) -> String {
2626
}
2727
let half_width = (max_width - 3) / 2;
2828
let end_start = status_len - half_width + (max_width - 3) % 2;
29-
return format!("{}...{}", &status[..half_width], &status[end_start..]);
29+
return format!("{}...{}", safe_string_trim_left(status.clone(), half_width),
30+
safe_string_trim_right(status.clone(), end_start));
3031
}
3132

3233
format!("{:width$}", status, width = max_width)

src/reader/file.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ impl Reader for FileReader{
129129

130130
#[inline]
131131
fn is_directory(url: &str) -> bool where Self: Sized {
132-
std::path::Path::new(url).is_dir()
132+
let path = Path::new(url);
133+
path.is_dir() && path.exists()
133134
}
134135

135136
fn get_size(&self) -> usize {
@@ -188,4 +189,17 @@ mod tests {
188189
let url = "/tmp/foo/bar/file";
189190
assert_eq!(FileReader::relative_path(src_arg, url), "tmp/foo/bar/file");
190191
}
192+
193+
#[test]
194+
fn test_relative_path_unicode() {
195+
let src_arg = "/tmp/документи";
196+
let url = "/tmp/документи/bar/file";
197+
assert_eq!(FileReader::relative_path(src_arg, url), "bar/file");
198+
let src_arg = "localdir/документи";
199+
let url = "localdir/документи/bar/file";
200+
assert_eq!(FileReader::relative_path(src_arg, url), "bar/file");
201+
let src_arg = "/";
202+
let url = "/tmp/документи/bar/file";
203+
assert_eq!(FileReader::relative_path(src_arg, url), "tmp/документи/bar/file");
204+
}
191205
}

src/utils.rs

+103-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,106 @@ pub fn get_time() -> u128{
1212
let start = SystemTime::now();
1313
start.duration_since(UNIX_EPOCH)
1414
.expect("Wrong system time").as_millis()
15-
}
15+
}
16+
17+
///
18+
/// Takes first `n` characters of string
19+
/// Works with Unicode
20+
///
21+
/// # Arguments
22+
///
23+
/// * `s`: string to trim
24+
/// * `n`: number of chars to take
25+
///
26+
/// returns: String: trimmed string
27+
///
28+
pub fn safe_string_trim_left(s: String, n: usize) -> String{
29+
let mut result: String = String::new();
30+
let mut r: usize = n;
31+
for c in s.chars(){
32+
if r == 0{
33+
break;
34+
}
35+
result.push(c);
36+
r -= 1;
37+
}
38+
result
39+
}
40+
41+
///
42+
/// Takes last `n` characters of string
43+
/// Works with Unicode
44+
///
45+
/// # Arguments
46+
///
47+
/// * `s`: string to trim
48+
/// * `n`: number of chars to take
49+
///
50+
/// returns: String: trimmed string
51+
///
52+
pub fn safe_string_trim_right(s: String, n: usize) -> String{
53+
let mut result: String = String::new();
54+
let mut r: usize = 0;
55+
for c in s.chars(){
56+
if r < n{
57+
r += 1;
58+
continue;
59+
}
60+
result.push(c);
61+
r += 1;
62+
}
63+
result
64+
}
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use super::*;
69+
70+
#[test]
71+
fn test_safe_string_trim_left_basic() {
72+
let input = String::from("Hello, World!");
73+
assert_eq!(safe_string_trim_left(input, 5), "Hello");
74+
}
75+
76+
#[test]
77+
fn test_safe_string_trim_left_unicode() {
78+
let input = String::from("🌍🚀🌌🛸");
79+
assert_eq!(safe_string_trim_left(input, 2), "🌍🚀");
80+
}
81+
82+
#[test]
83+
fn test_safe_string_trim_left_empty_string() {
84+
let input = String::from("");
85+
assert_eq!(safe_string_trim_left(input, 3), "");
86+
}
87+
88+
#[test]
89+
fn test_safe_string_trim_left_more_chars_than_length() {
90+
let input = String::from("Short");
91+
assert_eq!(safe_string_trim_left(input, 10), "Short");
92+
}
93+
94+
#[test]
95+
fn test_safe_string_trim_right_basic() {
96+
let input = String::from("Hello, World!");
97+
assert_eq!(safe_string_trim_right(input, 6), " World!");
98+
}
99+
100+
#[test]
101+
fn test_safe_string_trim_right_unicode() {
102+
let input = String::from("🌍🚀🌌🛸");
103+
assert_eq!(safe_string_trim_right(input, 2), "🌌🛸");
104+
}
105+
106+
#[test]
107+
fn test_safe_string_trim_right_empty_string() {
108+
let input = String::from("");
109+
assert_eq!(safe_string_trim_right(input, 3), "");
110+
}
111+
112+
#[test]
113+
fn test_safe_string_trim_right_more_chars_than_length() {
114+
let input = String::from("Short");
115+
assert_eq!(safe_string_trim_right(input, 10), "");
116+
}
117+
}

0 commit comments

Comments
 (0)