Skip to content

Commit

Permalink
Merge branch 'release/0.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
lucidfrontier45 committed Sep 29, 2023
2 parents 009f18a + b398004 commit 1757ca4
Show file tree
Hide file tree
Showing 12 changed files with 56 additions and 82 deletions.
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "serdeio"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
license = "MIT"
authors = ["Du Shiqiao <[email protected]>"]
Expand All @@ -13,14 +13,13 @@ categories = ["io-util", "serialization"]

[lib]
name = "serdeio"
doctest = false

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = { version = "0.9", optional = true }
csv = { version = "1", optional = true }

[dev-dependencies]
anyhow = "1"

[features]
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ Note that some data format like CSV and JSON Lines support only reading records
The following code read a JSON file and parse it as `Vec<User>`. Then it encodes the data into YAML format and write it to STDOUT.

```rust
use anyhow::{anyhow, Context, Result as AnyResult};
use serde::{Deserialize, Serialize};
use serdeio::{read_records_from_file, write_records_to_writer, DataFormat};
use serdeio::{read_record_from_file, write_records_to_writer, DataFormat};

#[derive(Debug, Deserialize, Serialize)]
struct User {
Expand All @@ -36,16 +37,19 @@ struct User {
items: Vec<String>,
}

fn main() {
pub fn main() -> AnyResult<()> {
// get input file path from argv
let args: Vec<String> = std::env::args().collect();
let input_file_path = &args[1];

// read to memory
let users: Vec<User> = read_records_from_file(input_file_path).unwrap();
// read json file to memory
let users: Vec<User> = read_record_from_file(input_file_path)
.context("Failed to read records from file")?;

// write to stdout in YAML format
// write to stdout in json lines format
let writer = std::io::stdout();
write_records_to_writer(writer, DataFormat::Yaml, &users).unwrap();
write_records_to_writer(writer, DataFormat::JsonLines, &users).unwrap();

Ok(())
}
```
14 changes: 7 additions & 7 deletions examples/anyhow.rs → examples/json2jsonl.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use anyhow::{anyhow, Context, Result as AnyResult};
use serde::{Deserialize, Serialize};
use serdeio::{read_records_from_file, write_records_to_writer, DataFormat};
use serdeio::{read_record_from_file, write_records_to_writer, DataFormat};

#[derive(Debug, Deserialize, Serialize)]
struct User {
id: u32,
name: String,
items: Vec<u32>,
items: Vec<String>,
}

fn main() -> AnyResult<()> {
pub fn main() -> AnyResult<()> {
// get input file path from argv
let args: Vec<String> = std::env::args().collect();
let input_file_path = &args[1];

// read to memory
let users: Vec<User> = read_records_from_file(input_file_path)
// read json to memory
let users: Vec<User> = read_record_from_file(input_file_path)
.map_err(|e| anyhow! {e})
.context("Failed to read records from file")?;

// write to stdout in YAML format
// write to stdout in json lines format
let writer = std::io::stdout();
write_records_to_writer(writer, DataFormat::Yaml, &users).unwrap();
write_records_to_writer(writer, DataFormat::JsonLines, &users).unwrap();

Ok(())
}
23 changes: 0 additions & 23 deletions examples/json2yaml.rs

This file was deleted.

7 changes: 3 additions & 4 deletions src/backend/csv.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::io::{Read, Write};

use anyhow::Result as AnyResult;
use serde::{de::DeserializeOwned, Serialize};

use crate::common::Result;

pub fn read<T: DeserializeOwned>(reader: impl Read) -> Result<Vec<T>> {
pub fn read<T: DeserializeOwned>(reader: impl Read) -> AnyResult<Vec<T>> {
let mut rdr = csv::Reader::from_reader(reader);
let mut records: Vec<T> = Vec::new();
for result in rdr.deserialize() {
Expand All @@ -14,7 +13,7 @@ pub fn read<T: DeserializeOwned>(reader: impl Read) -> Result<Vec<T>> {
Ok(records)
}

pub fn write<T: Serialize>(writer: impl Write, records: &[T]) -> Result<()> {
pub fn write<T: Serialize>(writer: impl Write, records: &[T]) -> AnyResult<()> {
let mut wtr = csv::Writer::from_writer(writer);
for record in records {
wtr.serialize(record)?;
Expand Down
7 changes: 3 additions & 4 deletions src/backend/json.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use std::io::{Read, Write};

use anyhow::Result as AnyResult;
use serde::{de::DeserializeOwned, Serialize};

use crate::common::Result;

pub fn read<T: DeserializeOwned>(reader: impl Read) -> Result<T> {
pub fn read<T: DeserializeOwned>(reader: impl Read) -> AnyResult<T> {
serde_json::from_reader(reader).map_err(|e| e.into())
}

pub fn write<T: Serialize>(writer: impl Write, record: &T) -> Result<()> {
pub fn write<T: Serialize>(writer: impl Write, record: &T) -> AnyResult<()> {
serde_json::to_writer(writer, record).map_err(|e| e.into())
}
7 changes: 3 additions & 4 deletions src/backend/jsonlines.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::io::{BufRead, BufReader, BufWriter, Read, Write};

use serde::{de::DeserializeOwned, Serialize};
use anyhow::Result as AnyResult;

use crate::common::Result;

pub fn read<T: DeserializeOwned>(reader: impl Read) -> Result<Vec<T>> {
pub fn read<T: DeserializeOwned>(reader: impl Read) -> AnyResult<Vec<T>> {
let reader = BufReader::new(reader);
let mut records: Vec<T> = Vec::new();
for line in reader.lines() {
Expand All @@ -15,7 +14,7 @@ pub fn read<T: DeserializeOwned>(reader: impl Read) -> Result<Vec<T>> {
Ok(records)
}

pub fn write<T: Serialize>(writer: impl Write, records: &[T]) -> Result<()> {
pub fn write<T: Serialize>(writer: impl Write, records: &[T]) -> AnyResult<()> {
let mut writer = BufWriter::new(writer);
for record in records {
let line = serde_json::to_string(record)?;
Expand Down
7 changes: 3 additions & 4 deletions src/backend/yaml.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use std::io::{Read, Write};

use anyhow::Result as AnyResult;
use serde::{de::DeserializeOwned, Serialize};

use crate::common::Result;

pub fn read<T: DeserializeOwned>(reader: impl Read) -> Result<T> {
pub fn read<T: DeserializeOwned>(reader: impl Read) -> AnyResult<T> {
serde_yaml::from_reader(reader).map_err(|e| e.into())
}

pub fn write<T: Serialize>(writer: impl Write, record: &T) -> Result<()> {
pub fn write<T: Serialize>(writer: impl Write, record: &T) -> AnyResult<()> {
serde_yaml::to_writer(writer, record).map_err(|e| e.into())
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#![doc = include_str!("../README.md")]

pub(crate) mod backend;
pub(crate) mod common;
pub(crate) mod types;
pub(crate) mod read;
pub(crate) mod write;

pub use common::DataFormat;
pub use types::DataFormat;
pub use read::{
read_record_from_file, read_record_from_reader, read_records_from_file,
read_records_from_reader,
Expand Down
16 changes: 7 additions & 9 deletions src/read.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
use std::{fs::File, io::Read, path::Path};

use anyhow::{anyhow, Result as AnyResult};
use serde::de::DeserializeOwned;

use crate::{
backend,
common::{DataFormat, Result},
};
use crate::{backend, types::DataFormat};

pub fn read_record_from_reader<T: DeserializeOwned>(
reader: impl Read,
data_format: DataFormat,
) -> Result<T> {
) -> AnyResult<T> {
match data_format {
DataFormat::Json => backend::json::read(reader),
#[cfg(feature = "yaml")]
DataFormat::Yaml => backend::yaml::read(reader),
_ => Err(format!("Unsupported file format: {}", data_format).into()),
_ => Err(anyhow!("Unsupported file format: {}", data_format)),
}
}

pub fn read_records_from_reader<T: DeserializeOwned>(
reader: impl Read,
data_format: DataFormat,
) -> Result<Vec<T>> {
) -> AnyResult<Vec<T>> {
match data_format {
DataFormat::Json => backend::json::read(reader),
DataFormat::JsonLines => backend::jsonlines::read(reader),
Expand All @@ -33,13 +31,13 @@ pub fn read_records_from_reader<T: DeserializeOwned>(
}
}

pub fn read_record_from_file<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<T> {
pub fn read_record_from_file<T: DeserializeOwned>(path: impl AsRef<Path>) -> AnyResult<T> {
let data_format = DataFormat::try_from(path.as_ref())?;
let file = File::open(path)?;
read_record_from_reader(file, data_format)
}

pub fn read_records_from_file<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<Vec<T>> {
pub fn read_records_from_file<T: DeserializeOwned>(path: impl AsRef<Path>) -> AnyResult<Vec<T>> {
let data_format = DataFormat::try_from(path.as_ref())?;
let file = File::open(path)?;
read_records_from_reader(file, data_format)
Expand Down
13 changes: 6 additions & 7 deletions src/common.rs → src/types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::{anyhow, Error as AnyError};
use std::{fmt::Display, path::Path};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
Expand All @@ -11,7 +12,7 @@ pub enum DataFormat {
}

impl TryFrom<&str> for DataFormat {
type Error = String;
type Error = AnyError;

fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match value.trim().to_lowercase().as_str() {
Expand All @@ -21,19 +22,19 @@ impl TryFrom<&str> for DataFormat {
"csv" => Ok(DataFormat::Csv),
#[cfg(feature = "yaml")]
"yaml" | "yml" => Ok(DataFormat::Yaml),
_ => Err(format!("Unknown data format: {}", value)),
_ => Err(anyhow!("Unknown data format: {}", value)),
}
}
}

impl TryFrom<&Path> for DataFormat {
type Error = String;
type Error = AnyError;

fn try_from(value: &Path) -> std::result::Result<Self, Self::Error> {
let ext = value
.extension()
.ok_or_else(|| format!("No extension found for file: {}", value.display()))
.and_then(|v| v.to_str().ok_or("Invalid extension".to_owned()))?;
.ok_or_else(|| anyhow!("No extension found for file: {}", value.display()))
.and_then(|v| v.to_str().ok_or(anyhow!("Invalid extension")))?;
Self::try_from(ext)
}
}
Expand All @@ -51,8 +52,6 @@ impl Display for DataFormat {
}
}

pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Sync + Send>>;

#[cfg(test)]
mod test {
#[test]
Expand Down
19 changes: 10 additions & 9 deletions src/write.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
use std::{fs::File, io::Write, path::Path};

use anyhow::{anyhow, Result as AnyResult};
use serde::Serialize;

use crate::{
backend,
common::{DataFormat, Result},
};
use crate::{backend, types::DataFormat};

pub fn write_record_to_writer<T: Serialize>(
writer: impl Write,
data_format: DataFormat,
record: &T,
) -> Result<()> {
) -> AnyResult<()> {
match data_format {
DataFormat::Json => backend::json::write(writer, record),
#[cfg(feature = "yaml")]
DataFormat::Yaml => backend::yaml::write(writer, record),
_ => Err(format!("Unsupported file format: {}", data_format).into()),
_ => Err(anyhow!("Unsupported file format: {}", data_format)),
}
}

pub fn write_records_to_writer<T: Serialize>(
writer: impl Write,
data_format: DataFormat,
records: &Vec<T>,
) -> Result<()> {
) -> AnyResult<()> {
match data_format {
DataFormat::Json => backend::json::write(writer, records),
DataFormat::JsonLines => backend::jsonlines::write(writer, records),
Expand All @@ -35,13 +33,16 @@ pub fn write_records_to_writer<T: Serialize>(
}
}

pub fn write_record_to_file<T: Serialize>(path: impl AsRef<Path>, records: &T) -> Result<()> {
pub fn write_record_to_file<T: Serialize>(path: impl AsRef<Path>, records: &T) -> AnyResult<()> {
let data_format = DataFormat::try_from(path.as_ref())?;
let file = File::create(path)?;
write_record_to_writer(file, data_format, records)
}

pub fn write_records_to_file<T: Serialize>(path: impl AsRef<Path>, records: &Vec<T>) -> Result<()> {
pub fn write_records_to_file<T: Serialize>(
path: impl AsRef<Path>,
records: &Vec<T>,
) -> AnyResult<()> {
let data_format = DataFormat::try_from(path.as_ref())?;
let file = File::create(path)?;
write_records_to_writer(file, data_format, records)
Expand Down

0 comments on commit 1757ca4

Please sign in to comment.