Initial commit
This commit is contained in:
commit
c387ea0685
4 changed files with 167 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
31
Cargo.lock
generated
Normal file
31
Cargo.lock
generated
Normal file
|
@ -0,0 +1,31 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "b32"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.hitchhiker-linux.org/jeang3nie/b32_rs.git#8ceaeff9b70aec6d271ba5f1be45d2c42e2894c3"
|
||||
|
||||
[[package]]
|
||||
name = "base32"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"b32",
|
||||
"getopts",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "base32"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
b32 = { git = "https://git.hitchhiker-linux.org/jeang3nie/b32_rs.git" }
|
||||
getopts = "0.2"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
strip = true
|
||||
|
121
src/main.rs
Normal file
121
src/main.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use {
|
||||
b32::{Decoder, Encoder},
|
||||
getopts::Options,
|
||||
std::{
|
||||
env,
|
||||
error::Error,
|
||||
fs::File,
|
||||
io::{self, BufReader, BufWriter, Write},
|
||||
os::fd::AsFd,
|
||||
path::Path
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Operation {
|
||||
Encode,
|
||||
Decode,
|
||||
}
|
||||
|
||||
enum Input {
|
||||
Stdin,
|
||||
Filename(String),
|
||||
}
|
||||
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let brief = format!("Usage: {} FILE [options]", program);
|
||||
print!("{}", opts.usage(&brief));
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = env::args().collect::<Vec<String>>();
|
||||
let progname = Path::new(&args[0])
|
||||
.file_name()
|
||||
.map(|x| x.to_string_lossy())
|
||||
.unwrap();
|
||||
let mut opts = Options::new();
|
||||
opts.optflag("h", "help", "print this help message");
|
||||
opts.optflag("d", "decode", "decode instead of encoding");
|
||||
opts.optflag("i", "ignore", "ignore whitespace when decoding");
|
||||
opts.optopt("o", "output", "output to FILE instead of STDOUT", "FILE");
|
||||
opts.optflag("q", "quiet", "never print headers giving file names");
|
||||
opts.optflag("v", "verbose", "always print headers giving file names");
|
||||
opts.optopt("w", "wrap", "wrap at columns", "COLS");
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
panic!("{}", f.to_string())
|
||||
}
|
||||
};
|
||||
if matches.opt_present("h") {
|
||||
print_usage(&progname, opts);
|
||||
return Ok(());
|
||||
}
|
||||
let op = if matches.opt_present("d") {
|
||||
Operation::Decode
|
||||
} else {
|
||||
Operation::Encode
|
||||
};
|
||||
let ignore = matches.opt_present("i");
|
||||
let wrap: usize = if let Some(w) = matches.opt_str("w") {
|
||||
w.parse()?
|
||||
} else {
|
||||
76
|
||||
};
|
||||
|
||||
let mut infiles = matches
|
||||
.free
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if x == "-" {
|
||||
Input::Stdin
|
||||
} else {
|
||||
Input::Filename(x.to_string())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if infiles.is_empty() {
|
||||
infiles.push(Input::Stdin)
|
||||
}
|
||||
if matches.opt_present("q") && matches.opt_present("v") {
|
||||
eprintln!("Error: conflicting options \"verbose\" and \"quiet\" are both present.");
|
||||
print_usage(&progname, opts);
|
||||
return Err("Conflicting options".into());
|
||||
}
|
||||
let verbose = matches.opt_present("v") || (matches.free.len() > 1 && !matches.opt_present("q"));
|
||||
for f in &infiles {
|
||||
let mut writer = if let Some(o) = matches.opt_str("o") {
|
||||
if matches.free.len() < 2 || op == Operation::Encode {
|
||||
BufWriter::new(File::open(o)?)
|
||||
} else {
|
||||
panic!("Attempt to decode multiple files into single file");
|
||||
}
|
||||
} else {
|
||||
BufWriter::new(File::from(io::stdout().as_fd().try_clone_to_owned()?))
|
||||
};
|
||||
let reader = match f {
|
||||
Input::Stdin => BufReader::new(File::from(io::stdin().as_fd().try_clone_to_owned()?)),
|
||||
Input::Filename(f) => BufReader::new(File::open(f)?),
|
||||
};
|
||||
if verbose {
|
||||
let fname = match f {
|
||||
Input::Stdin => "Stdin",
|
||||
Input::Filename(f) => f,
|
||||
};
|
||||
writer.write_fmt(format_args!("==> {fname} <==\n"))?;
|
||||
}
|
||||
match op {
|
||||
Operation::Decode => {
|
||||
let decoder = Decoder::new(reader, writer, None, ignore);
|
||||
decoder.decode()?;
|
||||
}
|
||||
Operation::Encode => {
|
||||
let mut encoder = Encoder::new(reader, writer, None, Some(wrap));
|
||||
encoder.encode()?;
|
||||
encoder.output().write_all(&[b'\n'])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Reference in a new issue