#![deny(missing_docs)]
#[cfg(feature = "tokio")]
extern crate tokio1 as tokio;
#[cfg(test)]
mod tests {
use std::error::Error;
cfg_if::cfg_if! {
if #[cfg(feature = "tokio")] {
use tokio_stream::StreamExt;
use tokio::fs::File;
} else {
use futures::stream::StreamExt;
use async_std::fs::File;
}
}
async fn create_async(file:&str) -> Result<(), Box<dyn Error>> {
let mut wri = crate::AsyncWriter::from_writer(
File::create(file).await?
);
wri.write_record(&["city","region","country","population","avg_age"]).await?;
wri.write_record(&["Northbridge","MA","United States","14061","42.5"]).await?;
wri.write_record(&["Westborough","MA","United States","29313", "45.1"]).await?;
wri.write_record(&["Springfield","NJ","United States","14976", "35.0"]).await?;
wri.flush().await?;
Ok(())
}
async fn copy_async(file_in:&str, file_out:&str) -> Result<(), Box<dyn Error>> {
let mut rdr = crate::AsyncReader::from_reader(
File::open(file_in).await?
);
let mut wri = crate::AsyncWriter::from_writer(
File::create(file_out).await?
);
wri.write_record(rdr.headers().await?.into_iter()).await?;
let mut records = rdr.records();
while let Some(record) = records.next().await {
wri.write_record(&record?).await?;
}
Ok(())
}
#[test]
fn test_on_files() {
use std::io::Read;
use std::hash::Hasher;
std::fs::create_dir_all("examples/data").unwrap();
let file_in = "examples/data/smallpop.csv";
let file_out = "examples/data/smallpop_out.csv";
#[cfg(not(feature = "tokio"))]
async_std::task::block_on(async {
if let Err(err) = create_async(file_in).await {
assert!(false, "error running create_async: {}", err);
}
if let Err(err) = copy_async(file_in, file_out).await {
assert!(false, "error running copy_async: {}", err);
}
});
#[cfg(feature = "tokio")]
tokio::runtime::Runtime::new().unwrap().block_on(async {
if let Err(err) = create_async(file_in).await {
assert!(false, "error running create_async: {}", err);
}
if let Err(err) = copy_async(file_in, file_out).await {
assert!(false, "error running copy_async: {}", err);
}
});
let mut bytes_in = vec![];
std::fs::File::open(file_in).unwrap().read_to_end(&mut bytes_in).unwrap();
let mut hasher_in = std::collections::hash_map::DefaultHasher::new();
hasher_in.write(&bytes_in);
let mut bytes_out = vec![];
std::fs::File::open(file_out).unwrap().read_to_end(&mut bytes_out).unwrap();
let mut hasher_out = std::collections::hash_map::DefaultHasher::new();
hasher_out.write(&bytes_out);
assert_eq!(hasher_in.finish(), hasher_out.finish(), "Copied file {} is different than source {}", file_out, file_in);
std::fs::remove_file(file_in).unwrap();
std::fs::remove_file(file_out).unwrap();
}
cfg_if::cfg_if! {
if #[cfg(feature = "with_serde")] {
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
struct Row {
city: String,
region: String,
country: String,
population: u64,
avg_age: f32,
}
async fn copy_async_serde(file_in:&str, file_out:&str) -> Result<(), Box<dyn Error>> {
let mut rdr = crate::AsyncDeserializer::from_reader(
File::open(file_in).await?
);
let mut wri = crate::AsyncSerializer::from_writer(
File::create(file_out).await?
);
let mut records = rdr.deserialize::<Row>();
while let Some(record) = records.next().await {
wri.serialize(&record?).await?;
}
Ok(())
}
#[test]
fn test_on_files_serde() {
use std::io::Read;
use std::hash::Hasher;
std::fs::create_dir_all("examples/data").unwrap();
let file_in = "examples/data/smallpop_serde.csv";
let file_out = "examples/data/smallpop_serde_out.csv";
#[cfg(not(feature = "tokio"))]
async_std::task::block_on(async {
if let Err(err) = create_async(file_in).await {
assert!(false, "error running create_async: {}", err);
}
if let Err(err) = copy_async_serde(file_in, file_out).await {
assert!(false, "error running copy_async_serde: {}", err);
}
});
#[cfg(feature = "tokio")]
tokio::runtime::Runtime::new().unwrap().block_on(async {
if let Err(err) = create_async(file_in).await {
assert!(false, "error running create_async: {}", err);
}
if let Err(err) = copy_async_serde(file_in, file_out).await {
assert!(false, "error running copy_async_serde: {}", err);
}
});
let mut bytes_in = vec![];
std::fs::File::open(file_in).unwrap().read_to_end(&mut bytes_in).unwrap();
let mut hasher_in = std::collections::hash_map::DefaultHasher::new();
hasher_in.write(&bytes_in);
let mut bytes_out = vec![];
std::fs::File::open(file_out).unwrap().read_to_end(&mut bytes_out).unwrap();
let mut hasher_out = std::collections::hash_map::DefaultHasher::new();
hasher_out.write(&bytes_out);
assert_eq!(hasher_in.finish(), hasher_out.finish(), "Copied file {} is different than source {}", file_out, file_in);
std::fs::remove_file(file_in).unwrap();
std::fs::remove_file(file_out).unwrap();
}
#[test]
#[cfg(not(tarpaulin))]
fn test_on_files_serde_send() {
use std::io::Read;
use std::hash::Hasher;
std::fs::create_dir_all("examples/data").unwrap();
let file_in = "examples/data/smallpop_serde_send.csv";
let file_out = "examples/data/smallpop_serde_send_out.csv";
#[cfg(not(feature = "tokio"))]
{
let jh = async_std::task::spawn(async move {
if let Err(err) = create_async(file_in).await {
assert!(false, "error running create_async: {}", err);
}
if let Err(err) = copy_async_serde(file_in, file_out).await {
assert!(false, "error running copy_async_serde: {}", err);
}
});
async_std::task::block_on(jh);
}
#[cfg(feature = "tokio")]
{
let rt = tokio::runtime::Runtime::new().unwrap();
let jh = rt.spawn(async move {
if let Err(err) = create_async(file_in).await {
assert!(false, "error running create_async: {}", err);
}
if let Err(err) = copy_async_serde(file_in, file_out).await {
assert!(false, "error running copy_async_serde: {}", err);
}
});
rt.block_on(jh).unwrap();
}
let mut bytes_in = vec![];
std::fs::File::open(file_in).unwrap().read_to_end(&mut bytes_in).unwrap();
let mut hasher_in = std::collections::hash_map::DefaultHasher::new();
hasher_in.write(&bytes_in);
let mut bytes_out = vec![];
std::fs::File::open(file_out).unwrap().read_to_end(&mut bytes_out).unwrap();
let mut hasher_out = std::collections::hash_map::DefaultHasher::new();
hasher_out.write(&bytes_out);
assert_eq!(hasher_in.finish(), hasher_out.finish(), "Copied file {} is different than source {}", file_out, file_in);
std::fs::remove_file(file_in).unwrap();
std::fs::remove_file(file_out).unwrap();
}
}
}
}
mod byte_record;
mod debug;
mod error;
mod string_record;
cfg_if::cfg_if! {
if #[cfg(feature = "with_serde")] {
mod deserializer;
mod serializer;
pub use deserializer::{DeserializeError, DeserializeErrorKind};
}}
mod async_readers;
mod async_writers;
pub use crate::byte_record::{ByteRecord, ByteRecordIter, Position};
pub use crate::error::{
Error, ErrorKind, FromUtf8Error, IntoInnerError, Result, Utf8Error,
};
pub use crate::string_record::{StringRecord, StringRecordIter};
pub use crate::async_readers::AsyncReaderBuilder;
pub use crate::async_writers::AsyncWriterBuilder;
cfg_if::cfg_if! {
if #[cfg(feature = "tokio")] {
pub use crate::async_readers::{
ardr_tokio::AsyncReader,
ByteRecordsIntoStream, ByteRecordsStream,
StringRecordsIntoStream, StringRecordsStream,
};
pub use crate::async_writers::awtr_tokio::AsyncWriter;
} else {
pub use crate::async_readers::{
ardr_futures::AsyncReader,
ByteRecordsIntoStream, ByteRecordsStream,
StringRecordsIntoStream, StringRecordsStream,
};
pub use crate::async_writers::awtr_futures::AsyncWriter;
}}
#[cfg(all(feature = "with_serde", not(feature = "tokio")))]
pub use crate::async_readers::{
ades_futures::AsyncDeserializer,
DeserializeRecordsStream, DeserializeRecordsIntoStream,
DeserializeRecordsStreamPos, DeserializeRecordsIntoStreamPos,
};
#[cfg(all(feature = "with_serde", not(feature = "tokio")))]
pub use crate::async_writers::aser_futures::AsyncSerializer;
#[cfg(all(feature = "with_serde", feature = "tokio"))]
pub use crate::async_readers::{
ades_tokio::AsyncDeserializer,
DeserializeRecordsStream, DeserializeRecordsIntoStream,
DeserializeRecordsStreamPos, DeserializeRecordsIntoStreamPos,
};
#[cfg(all(feature = "with_serde", feature = "tokio"))]
pub use crate::async_writers::aser_tokio::AsyncSerializer;
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum QuoteStyle {
Always,
Necessary,
NonNumeric,
Never,
}
impl QuoteStyle {
#[allow(unreachable_patterns)]
fn to_core(self) -> csv_core::QuoteStyle {
match self {
QuoteStyle::Always => csv_core::QuoteStyle::Always,
QuoteStyle::Necessary => csv_core::QuoteStyle::Necessary,
QuoteStyle::NonNumeric => csv_core::QuoteStyle::NonNumeric,
QuoteStyle::Never => csv_core::QuoteStyle::Never
}
}
}
impl Default for QuoteStyle {
fn default() -> QuoteStyle {
QuoteStyle::Necessary
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum Terminator {
CRLF,
Any(u8),
}
impl Terminator {
#[allow(unreachable_patterns)]
fn to_core(self) -> csv_core::Terminator {
match self {
Terminator::CRLF => csv_core::Terminator::CRLF,
Terminator::Any(b) => csv_core::Terminator::Any(b)
}
}
}
impl Default for Terminator {
fn default() -> Terminator {
Terminator::CRLF
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum Trim {
None,
Headers,
Fields,
All,
}
impl Trim {
fn should_trim_fields(&self) -> bool {
self == &Trim::Fields || self == &Trim::All
}
fn should_trim_headers(&self) -> bool {
self == &Trim::Headers || self == &Trim::All
}
}
impl Default for Trim {
fn default() -> Trim {
Trim::None
}
}