cli dollcode encoder and decoder
1use clap::{ArgGroup, Command, Id, arg, command, crate_authors};
2use lib_porcelain::Dollcode;
3#[cfg(feature = "bigint")]
4use num::{BigUint, Num, traits::ToBytes};
5use std::str::FromStr;
6
7fn main() {
8 let matches = command!()
9 .author(crate_authors!("\n"))
10 .arg(arg!(-v --verbose ... "provide more info"))
11 .subcommand_required(true)
12 .subcommand(
13 Command::new("encode")
14 .arg(arg!([input] "Input to encode to a dollcode").required(true))
15 .arg(arg!(hex: -x --hex "Encode from hex"))
16 .arg(arg!(string: -s --string "Encode from a utf8 string")),
17 )
18 .subcommand(
19 Command::new("decode")
20 .arg(arg!([dollcode] "Dollcode to decode").required(true))
21 .arg(arg!(decimial: -d --decimial "Decode to decimial"))
22 .arg(arg!(hex: -x --hex "Decode to hexadecimal"))
23 .arg(arg!(string: -s --string "Decode to utf8 string"))
24 .group(ArgGroup::new("format").args(["decimial", "hex", "string"])),
25 )
26 .get_matches();
27
28 if let Some(ematches) = matches.subcommand_matches("encode") {
29 let input = ematches.get_one::<String>("input").unwrap();
30
31 if ematches.get_flag("string") {
32 #[cfg(feature = "bigint")]
33 println!(
34 "{}",
35 BigUint::from_bytes_be(input.clone().as_bytes()).to_dollcode()
36 );
37 #[cfg(not(feature = "bigint"))]
38 println!("{}", {
39 let mut buf = [0u8; 8];
40 let len = 8.min(input.len());
41 buf[..len].copy_from_slice(&input.as_bytes()[..len]);
42 u64::from_be_bytes(buf).to_dollcode()
43 });
44
45 return;
46 }
47
48 if let Some(s) = input.strip_prefix("0x") {
49 #[cfg(feature = "bigint")]
50 println!(
51 "{}",
52 BigUint::from_str_radix(s, 16)
53 .expect("invalid hex")
54 .to_dollcode()
55 );
56 #[cfg(not(feature = "bigint"))]
57 println!(
58 "{}",
59 u64::from_str_radix(s, 16)
60 .expect("invalid hex")
61 .to_dollcode()
62 );
63 return;
64 }
65
66 if ematches.get_flag("hex") {
67 #[cfg(feature = "bigint")]
68 println!(
69 "{}",
70 BigUint::from_str_radix(input, 16)
71 .expect("invalid hex")
72 .to_dollcode()
73 );
74 #[cfg(not(feature = "bigint"))]
75 println!(
76 "{}",
77 u64::from_str_radix(input, 16)
78 .expect("invalid hex")
79 .to_dollcode(),
80 );
81 return;
82 }
83
84 #[cfg(feature = "bigint")]
85 println!(
86 "{}",
87 BigUint::from_str(input)
88 .expect("input must be declared as hex or be a decimial number")
89 .to_dollcode()
90 );
91 #[cfg(not(feature = "bigint"))]
92 println!(
93 "{}",
94 input
95 .parse::<u64>()
96 .expect("input must be declared as hex or be a decimial number")
97 .to_dollcode()
98 );
99 }
100
101 if let Some(dmatches) = matches.subcommand_matches("decode") {
102 let dollcode = dmatches.get_one::<String>("dollcode").unwrap();
103
104 let err = "dollcodes may only contain '▖', '▘', or '▌'";
105
106 #[cfg(feature = "bigint")]
107 let res = BigUint::from_dollcode(dollcode).expect(err);
108 #[cfg(not(feature = "bigint"))]
109 let res = u64::from_dollcode(dollcode).expect(err);
110
111 let res_bytes = res.clone().to_be_bytes();
112
113 match dmatches.get_one("format").map(Id::as_ref) {
114 Some("decimial") => println!("{}", res),
115 Some("hex") => println!("{:X}", res),
116 Some("string") => println!(
117 "{}",
118 str::from_utf8(&res_bytes).expect("failed to encode res as utf8")
119 ),
120 None => println!(
121 "decimial: {}, hexadecimal: {:X}, string: {}",
122 res,
123 res,
124 match str::from_utf8(&res_bytes) {
125 Ok(v) => v.to_string(),
126 Err(e) =>
127 if *matches
128 .get_one::<u8>("verbose")
129 .expect("error getting verbose level")
130 > 0
131 {
132 format!("invalid utf8: {:?}", e)
133 } else {
134 "invalid utf8".to_string()
135 },
136 }
137 ),
138 Some(_) => unreachable!(),
139 }
140 }
141}