Skip to content

Commit 92fb11b

Browse files
authored
Merge pull request #2 from dodomorandi/multiplatform_lines
Split lines according to both dos and unix conventions
2 parents b7b14d4 + fc90c3d commit 92fb11b

File tree

2 files changed

+67
-18
lines changed

2 files changed

+67
-18
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ chrono = "0.4.11"
2929
tokio = { version = "0.2.21", features = ["tcp", "dns", "io-util"] }
3030
tokio-rustls = { version = "0.13.1", optional = true }
3131
pin-project = "0.4.17"
32+
33+
[dev-dependencies.tokio]
34+
version = "0.2.21"
35+
features = [ "macros", "stream", "io-util" ]

src/ftp.rs

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use chrono::{DateTime, Utc};
99
use regex::Regex;
1010

1111
use tokio::io::{
12-
copy, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter,
12+
copy, AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWriteExt, BufReader,
13+
BufWriter,
1314
};
1415
use tokio::net::{TcpStream, ToSocketAddrs};
1516

@@ -435,28 +436,38 @@ impl FtpStream {
435436
open_code: u32,
436437
close_code: &[u32],
437438
) -> Result<Vec<String>> {
438-
let mut lines: Vec<String> = Vec::new();
439-
440-
let mut data_stream = BufReader::new(self.data_command(&cmd).await?);
439+
let data_stream = BufReader::new(self.data_command(&cmd).await?);
441440
self.read_response_in(&[open_code, status::ALREADY_OPEN])
442441
.await?;
442+
let lines = Self::get_lines_from_stream(data_stream).await?;
443+
self.read_response_in(close_code).await?;
444+
Ok(lines)
445+
}
443446

444-
let mut line = String::new();
447+
/// Consume a stream and return a vector of lines
448+
async fn get_lines_from_stream<R>(data_stream: R) -> Result<Vec<String>>
449+
where
450+
R: AsyncBufRead + Unpin,
451+
{
452+
let mut lines: Vec<String> = Vec::new();
453+
454+
let mut lines_stream = data_stream.lines();
445455
loop {
446-
match data_stream.read_to_string(&mut line).await {
447-
Ok(0) => break,
448-
Ok(_) => lines.extend(
449-
line.split("\r\n")
450-
.map(String::from)
451-
.filter(|s| !s.is_empty()),
452-
),
453-
Err(err) => return Err(FtpError::ConnectionError(err)),
454-
};
455-
}
456-
drop(data_stream);
456+
let line = lines_stream
457+
.next_line()
458+
.await
459+
.map_err(FtpError::ConnectionError)?;
457460

458-
self.read_response_in(close_code).await?;
459-
Ok(lines)
461+
match line {
462+
Some(line) => {
463+
if line.is_empty() {
464+
continue;
465+
}
466+
lines.push(line);
467+
}
468+
None => break Ok(lines),
469+
}
470+
}
460471
}
461472

462473
/// Execute `LIST` command which returns the detailed file listing in human readable format.
@@ -597,3 +608,37 @@ impl FtpStream {
597608
}
598609
}
599610
}
611+
612+
#[cfg(test)]
613+
mod tests {
614+
use super::FtpStream;
615+
use tokio::{io::stream_reader, stream};
616+
617+
#[tokio::test]
618+
async fn list_command_dos_newlines() {
619+
let data_stream = stream_reader(stream::once(Ok(
620+
b"Hello\r\nWorld\r\n\r\nBe\r\nHappy\r\n" as &[u8]
621+
)));
622+
623+
assert_eq!(
624+
FtpStream::get_lines_from_stream(data_stream).await.unwrap(),
625+
["Hello", "World", "Be", "Happy"]
626+
.iter()
627+
.map(<&str>::to_string)
628+
.collect::<Vec<_>>()
629+
);
630+
}
631+
632+
#[tokio::test]
633+
async fn list_command_unix_newlines() {
634+
let data_stream = stream_reader(stream::once(Ok(b"Hello\nWorld\n\nBe\nHappy\n" as &[u8])));
635+
636+
assert_eq!(
637+
FtpStream::get_lines_from_stream(data_stream).await.unwrap(),
638+
["Hello", "World", "Be", "Happy"]
639+
.iter()
640+
.map(<&str>::to_string)
641+
.collect::<Vec<_>>()
642+
);
643+
}
644+
}

0 commit comments

Comments
 (0)