Skip to content

Commit

Permalink
Add cli utillity
Browse files Browse the repository at this point in the history
  • Loading branch information
matusf committed Feb 27, 2024
1 parent 6b593d3 commit a986972
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 6 deletions.
180 changes: 180 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ categories = ["encoding"]
name = "zbase32"
crate-type = ["cdylib", "rlib"]

[[bin]]
name = "zbase32"
required-features = ["cli"]

[features]
python = ["pyo3"]
python = ["dep:pyo3"]
cli = ["dep:clap", "dep:anyhow"]

[dependencies]

[dependencies.pyo3]
version = "0.20.2"
features = ["extension-module"]
optional = true
pyo3 = {version = "0.20.2", features = ["extension-module"], optional = true}
clap = {version = "4.5.1", features = ["derive"], optional = true}
anyhow = {version = "1", optional = true}

[dev-dependencies]
quickcheck = "1"
92 changes: 92 additions & 0 deletions src/bin/zbase32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use std::{
fs::File,
io::{self, BufRead, Read, Write},
path::PathBuf,
};

use anyhow::Result;
use clap::Parser;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Decode data
#[arg(short, long)]
decode: bool,

/// File to encode or decode
file: Option<PathBuf>,

/// Wrap encoded lines after COLS character. Use 0 to disable line wrapping
#[arg(short, long, default_value_t = 76)]
wrap: usize,
}

fn decode(reader: impl Read, writer: &mut impl Write) -> Result<()> {
let buf: Result<Vec<String>, std::io::Error> = io::BufReader::new(reader).lines().collect();
let buf: String = buf?
.into_iter()
.map(|mut line| {
line.truncate(line.trim_end().len());
line
})
.collect();

writer.write_all(&zbase32::decode(&buf)?)?;
Ok(())
}

fn encode(reader: &mut impl Read, mut writer: &mut impl Write, wrap: usize) -> Result<()> {
let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
let buf = zbase32::encode(&buf);
if wrap > 0 {
buf.as_bytes()
.chunks(wrap)
.map(|chunk| {
let chars = unsafe { std::str::from_utf8_unchecked(chunk) };
write!(&mut writer, "{}\n", chars)
})
.collect::<Result<Vec<()>, _>>()?;
} else {
writer.write_all(buf.as_bytes())?;
writer.write(b"\n")?;
}
return Ok(());
}

fn main() -> Result<()> {
let args = Args::parse();

if args.decode {
if let Some(file) = args.file {
decode(File::open(&file)?, &mut io::stdout())?;
} else {
decode(io::stdin().lock(), &mut io::stdout())?;
};
} else {
if let Some(file) = args.file {
encode(&mut File::open(&file)?, &mut io::stdout(), args.wrap)
} else {
encode(&mut io::stdin().lock(), &mut io::stdout(), args.wrap)
}?;
};

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use quickcheck::quickcheck;

quickcheck! {
fn prop(input: Vec<u8>, wrap: usize) -> bool {
let mut encoded = Vec::new();
let mut decoded = Vec::new();
encode(&mut input.as_slice(), &mut encoded, wrap).unwrap();
decode(encoded.as_slice(), &mut decoded).unwrap();
decoded == input
}
}
}

0 comments on commit a986972

Please sign in to comment.