use clap::{ArgGroup, Command, Id, arg, command, crate_authors}; use lib_porcelain::Dollcode; #[cfg(feature = "bigint")] use num::{BigUint, Num, traits::ToBytes}; use std::str::FromStr; fn main() { let matches = command!() .author(crate_authors!("\n")) .arg(arg!(-v --verbose ... "provide more info")) .subcommand_required(true) .subcommand( Command::new("encode") .arg(arg!([input] "Input to encode to a dollcode").required(true)) .arg(arg!(hex: -x --hex "Encode from hex")) .arg(arg!(string: -s --string "Encode from a utf8 string")), ) .subcommand( Command::new("decode") .arg(arg!([dollcode] "Dollcode to decode").required(true)) .arg(arg!(decimial: -d --decimial "Decode to decimial")) .arg(arg!(hex: -x --hex "Decode to hexadecimal")) .arg(arg!(string: -s --string "Decode to utf8 string")) .group(ArgGroup::new("format").args(["decimial", "hex", "string"])), ) .get_matches(); if let Some(ematches) = matches.subcommand_matches("encode") { let input = ematches.get_one::("input").unwrap(); if ematches.get_flag("string") { #[cfg(feature = "bigint")] println!( "{}", BigUint::from_bytes_be(input.clone().as_bytes()).to_dollcode() ); #[cfg(not(feature = "bigint"))] println!("{}", { let mut buf = [0u8; 8]; let len = 8.min(input.len()); buf[..len].copy_from_slice(&input.as_bytes()[..len]); u64::from_be_bytes(buf).to_dollcode() }); return; } if let Some(s) = input.strip_prefix("0x") { #[cfg(feature = "bigint")] println!( "{}", BigUint::from_str_radix(s, 16) .expect("invalid hex") .to_dollcode() ); #[cfg(not(feature = "bigint"))] println!( "{}", u64::from_str_radix(s, 16) .expect("invalid hex") .to_dollcode() ); return; } if ematches.get_flag("hex") { #[cfg(feature = "bigint")] println!( "{}", BigUint::from_str_radix(input, 16) .expect("invalid hex") .to_dollcode() ); #[cfg(not(feature = "bigint"))] println!( "{}", u64::from_str_radix(input, 16) .expect("invalid hex") .to_dollcode(), ); return; } #[cfg(feature = "bigint")] println!( "{}", BigUint::from_str(input) .expect("input must be declared as hex or be a decimial number") .to_dollcode() ); #[cfg(not(feature = "bigint"))] println!( "{}", input .parse::() .expect("input must be declared as hex or be a decimial number") .to_dollcode() ); } if let Some(dmatches) = matches.subcommand_matches("decode") { let dollcode = dmatches.get_one::("dollcode").unwrap(); let err = "dollcodes may only contain '▖', '▘', or '▌'"; #[cfg(feature = "bigint")] let res = BigUint::from_dollcode(dollcode).expect(err); #[cfg(not(feature = "bigint"))] let res = u64::from_dollcode(dollcode).expect(err); let res_bytes = res.clone().to_be_bytes(); match dmatches.get_one("format").map(Id::as_ref) { Some("decimial") => println!("{}", res), Some("hex") => println!("{:X}", res), Some("string") => println!( "{}", str::from_utf8(&res_bytes).expect("failed to encode res as utf8") ), None => println!( "decimial: {}, hexadecimal: {:X}, string: {}", res, res, match str::from_utf8(&res_bytes) { Ok(v) => v.to_string(), Err(e) => if *matches .get_one::("verbose") .expect("error getting verbose level") > 0 { format!("invalid utf8: {:?}", e) } else { "invalid utf8".to_string() }, } ), Some(_) => unreachable!(), } } }